@c15t/react 3.0.0-alpha.1 → 3.0.0-alpha.3
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 +9 -2
- package/dist/aggregate-components.js +1 -1
- package/dist/chunk-warming.js +1 -1
- package/dist/component-hooks/use-headless-iab-consent-ui.js +1 -1
- package/dist/component-hooks/use-panel-trigger.js +1 -1
- package/dist/components/consent-gate/atoms.js +1 -0
- package/dist/components/consent-gate/consent-gate.js +2 -0
- package/dist/components/consent-gate/index.js +2 -0
- package/dist/components/consent-preferences-link/consent-preferences-link.js +1 -1
- package/dist/components/iab-panel/atoms/purpose-item.js +1 -1
- package/dist/components/iab-panel/atoms/stack-item.js +1 -1
- package/dist/components/iab-panel/iab-panel.js +1 -1
- package/dist/components/iab-panel/index.js +1 -1
- package/dist/components/panel/atoms/overlay.js +1 -1
- package/dist/components/panel/atoms/root.js +1 -1
- package/dist/components/panel/index.js +1 -1
- package/dist/components/panel/panel.js +1 -1
- package/dist/components/panel-trigger/atoms/button.js +1 -1
- package/dist/components/panel-trigger/atoms/icon.js +1 -1
- package/dist/components/panel-trigger/atoms/root.js +1 -1
- package/dist/components/panel-trigger/atoms/trigger-icons.js +1 -0
- package/dist/components/panel-trigger/panel-trigger-toolbar.js +1 -1
- package/dist/components/preferences/atoms/accordion.js +1 -1
- package/dist/components/preferences/atoms/root.js +1 -1
- package/dist/components/preferences/atoms/vendor-list.js +1 -0
- package/dist/components/preferences/index.js +1 -1
- package/dist/components/prompt/policy-actions.js +1 -1
- package/dist/components/shared/primitives/button.js +1 -1
- package/dist/components/shared/ui/accordion/accordion.js +1 -1
- package/dist/components/shared/ui/collapsible/collapsible.js +1 -1
- package/dist/components/shared/ui/logo.js +1 -1
- package/dist/components/shared/ui/preference-item/preference-item.js +1 -1
- package/dist/components/shared/ui/switch/switch.js +1 -1
- package/dist/components/shared/ui/tabs/tabs.js +1 -1
- package/dist/consent-gate.js +2 -0
- package/dist/consent-theme.js +1 -0
- package/dist/draft.js +1 -1
- package/dist/external-iab-context.js +1 -1
- package/dist/headless.js +1 -1
- package/dist/hooks/use-committed-ref.js +2 -0
- package/dist/hooks.js +1 -1
- package/dist/iab/styles.tw3.css +1 -41
- package/dist/iab-context.js +1 -1
- package/dist/index.js +1 -1
- package/dist/kernel-selector.js +1 -0
- package/dist/module-hooks/iframe-blocker.js +1 -1
- package/dist/module-hooks/network-blocker.js +1 -1
- package/dist/module-hooks/network-hold.js +2 -0
- package/dist/module-hooks/persistence.js +1 -1
- package/dist/panel.js +1 -1
- package/dist/preferences.js +1 -2
- package/dist/provider.js +1 -1
- package/dist/styles.tw3.css +1 -43
- package/dist/ui-save.js +1 -1
- package/dist/utils/deferred-module.js +2 -0
- package/dist/version.js +1 -1
- package/dist-types/aggregate-components.d.ts +2 -2
- package/dist-types/chunk-warming.d.ts +41 -8
- package/dist-types/component-hooks/use-headless-iab-consent-ui.d.ts +2 -2
- package/dist-types/components/consent-gate/atoms.d.ts +11 -0
- package/dist-types/components/consent-gate/consent-gate.d.ts +2 -0
- package/dist-types/components/consent-gate/index.d.ts +41 -0
- package/dist-types/components/{frame → consent-gate}/types.d.ts +7 -7
- package/dist-types/components/consent-preferences-link/consent-preferences-link.d.ts +1 -1
- package/dist-types/components/consent-preferences-link/index.d.ts +1 -1
- package/dist-types/components/iab-panel/atoms/purpose-item.d.ts +5 -2
- package/dist-types/components/iab-panel/atoms/stack-item.d.ts +7 -2
- package/dist-types/components/iab-panel/atoms/vendor-list.d.ts +1 -1
- package/dist-types/components/iab-panel/iab-panel.d.ts +1 -1
- package/dist-types/components/iab-panel/index.d.ts +25 -24
- package/dist-types/components/iab-prompt/index.d.ts +17 -17
- package/dist-types/components/panel/atoms/card.d.ts +4 -4
- package/dist-types/components/panel/atoms/overlay.d.ts +1 -1
- package/dist-types/components/panel/index.d.ts +8 -7
- package/dist-types/components/panel/panel.d.ts +3 -3
- package/dist-types/components/panel-link/index.d.ts +1 -1
- package/dist-types/components/panel-trigger/atoms/button.d.ts +1 -1
- package/dist-types/components/panel-trigger/atoms/icon.d.ts +1 -1
- package/dist-types/components/panel-trigger/atoms/root.d.ts +2 -2
- package/dist-types/components/panel-trigger/atoms/toolbar.d.ts +2 -2
- package/dist-types/components/panel-trigger/atoms/trigger-icons.d.ts +20 -0
- package/dist-types/components/panel-trigger/index.d.ts +4 -4
- package/dist-types/components/panel-trigger/panel-trigger-toolbar.d.ts +1 -1
- package/dist-types/components/panel-trigger/panel-trigger.d.ts +9 -9
- package/dist-types/components/panel-trigger/types.d.ts +1 -1
- package/dist-types/components/preferences/atoms/accordion.d.ts +6 -4
- package/dist-types/components/preferences/atoms/button.d.ts +1 -1
- package/dist-types/components/preferences/atoms/footer.d.ts +1 -1
- package/dist-types/components/preferences/atoms/root.d.ts +1 -1
- package/dist-types/components/preferences/atoms/vendor-list.d.ts +23 -0
- package/dist-types/components/preferences/index.d.ts +14 -11
- package/dist-types/components/preferences/policy-actions.d.ts +2 -2
- package/dist-types/components/preferences/preferences.d.ts +1 -1
- package/dist-types/components/preferences/types.d.ts +1 -1
- package/dist-types/components/prompt/components.d.ts +3 -3
- package/dist-types/components/prompt/index.d.ts +11 -11
- package/dist-types/components/prompt/policy-actions.d.ts +2 -2
- package/dist-types/components/prompt/prompt.d.ts +1 -1
- package/dist-types/components/shared/policy-actions.d.ts +2 -2
- package/dist-types/components/shared/primitives/box.d.ts +1 -1
- package/dist-types/components/shared/primitives/button.d.ts +2 -2
- package/dist-types/components/shared/primitives/button.types.d.ts +1 -1
- package/dist-types/components/shared/ui/accordion/accordion.d.ts +3 -2
- package/dist-types/components/shared/ui/accordion/index.d.ts +2 -2
- package/dist-types/components/shared/ui/animated-collapse/index.d.ts +2 -2
- package/dist-types/components/shared/ui/branding.d.ts +1 -1
- package/dist-types/components/shared/ui/button/button.d.ts +1 -1
- package/dist-types/components/shared/ui/button/index.d.ts +2 -2
- package/dist-types/components/shared/ui/collapsible/collapsible.d.ts +1 -0
- package/dist-types/components/shared/ui/collapsible/index.d.ts +1 -1
- package/dist-types/components/shared/ui/dialog/index.d.ts +1 -1
- package/dist-types/components/shared/ui/logo.d.ts +0 -10
- package/dist-types/components/shared/ui/preference-item/index.d.ts +1 -1
- package/dist-types/components/shared/ui/preference-item/preference-item.d.ts +14 -0
- package/dist-types/components/shared/ui/switch/index.d.ts +2 -2
- package/dist-types/components/shared/ui/switch/switch.d.ts +2 -1
- package/dist-types/components/shared/ui/tabs/index.d.ts +1 -1
- package/dist-types/components/shared/ui/tabs/tabs.d.ts +1 -0
- package/dist-types/consent-gate.d.ts +1 -0
- package/dist-types/consent-theme.d.ts +38 -0
- package/dist-types/context/index.d.ts +3 -3
- package/dist-types/context/theme-context.d.ts +1 -1
- package/dist-types/draft.d.ts +96 -7
- package/dist-types/headless.d.ts +6 -7
- package/dist-types/hooks/use-committed-ref.d.ts +24 -0
- package/dist-types/hooks/use-headless-iab-consent-ui.d.ts +1 -1
- package/dist-types/hooks/use-iab-manager.d.ts +1 -1
- package/dist-types/hooks/use-theme.d.ts +1 -1
- package/dist-types/hooks.d.ts +30 -1
- package/dist-types/iab.d.ts +5 -5
- package/dist-types/index.d.ts +26 -25
- package/dist-types/kernel-selector.d.ts +66 -0
- package/dist-types/module-hooks/iframe-blocker.d.ts +27 -0
- package/dist-types/module-hooks/network-blocker.d.ts +23 -0
- package/dist-types/module-hooks/network-hold.d.ts +47 -0
- package/dist-types/module-hooks.d.ts +4 -4
- package/dist-types/panel-link.d.ts +1 -1
- package/dist-types/panel-trigger.d.ts +1 -1
- package/dist-types/panel.d.ts +13 -1
- package/dist-types/preferences.d.ts +12 -1
- package/dist-types/primitives/accordion.d.ts +1 -1
- package/dist-types/primitives/button.d.ts +1 -1
- package/dist-types/primitives/collapsible.d.ts +1 -1
- package/dist-types/primitives/dialog.d.ts +1 -1
- package/dist-types/primitives/preference-item.d.ts +1 -1
- package/dist-types/primitives/switch.d.ts +1 -1
- package/dist-types/primitives/tabs.d.ts +1 -1
- package/dist-types/primitives.d.ts +7 -7
- package/dist-types/prompt.d.ts +1 -1
- package/dist-types/provider.d.ts +62 -12
- package/dist-types/server/fetch-ssr-data.d.ts +1 -1
- package/dist-types/server/index.d.ts +5 -5
- package/dist-types/theme-provider.d.ts +2 -2
- package/dist-types/types/index.d.ts +3 -3
- package/dist-types/types/manager.d.ts +1 -1
- package/dist-types/types/theme/index.d.ts +1 -1
- package/dist-types/ui-config-context.d.ts +3 -1
- package/dist-types/ui-save.d.ts +21 -1
- package/dist-types/utils/deferred-module.d.ts +13 -0
- package/dist-types/utils/index.d.ts +4 -4
- package/dist-types/utils/merge-slot-props.d.ts +2 -2
- package/dist-types/utils/merge-styles.d.ts +1 -1
- package/dist-types/utils/theme-utils.d.ts +1 -1
- package/dist-types/version.d.ts +1 -1
- package/docs/README.md +9 -2
- package/docs/customization/overview.md +4 -3
- package/docs/customization/recipes.md +4 -2
- package/docs/customization/tokens.md +66 -3
- package/docs/frameworks/react/components/consent-dialog.md +46 -7
- package/docs/frameworks/react/components/{frame.md → consent-gate.md} +35 -31
- package/docs/frameworks/react/components/consent-manager-provider.md +35 -11
- package/docs/frameworks/react/components/consent-widget.md +48 -6
- package/docs/frameworks/react/concepts/consent-categories.md +38 -2
- package/docs/frameworks/react/hooks/{use-consent-manager/overview.md → overview.md} +7 -5
- package/docs/frameworks/react/iab/overview.md +121 -1
- package/docs/frameworks/react/network-blocker.md +146 -0
- package/docs/frameworks/react/script-loader.md +6 -0
- package/docs/frameworks/react/styling/overview.md +71 -22
- package/docs/guides/consent-state.md +327 -0
- package/docs/guides/deployment-modes.md +12 -0
- package/docs/guides/shared-consent-controls.md +158 -0
- package/docs/integrations/adobe-analytics.md +1 -1
- package/docs/integrations/ahrefs-analytics.md +1 -1
- package/docs/integrations/amplitude.md +1 -1
- package/docs/integrations/building-integrations.md +5 -0
- package/docs/integrations/clearbit.md +1 -1
- package/docs/integrations/cloudflare-web-analytics.md +1 -1
- package/docs/integrations/cloudflare-zaraz.md +399 -0
- package/docs/integrations/crisp.md +1 -1
- package/docs/integrations/databuddy.md +1 -1
- package/docs/integrations/fathom-analytics.md +1 -1
- package/docs/integrations/front-chat.md +322 -0
- package/docs/integrations/google-maps.md +21 -21
- package/docs/integrations/google-tag-manager.md +1 -1
- package/docs/integrations/google-tag.md +1 -1
- package/docs/integrations/granular-consent.md +210 -0
- package/docs/integrations/heap.md +1 -1
- package/docs/integrations/hightouch.md +1 -1
- package/docs/integrations/hotjar.md +1 -1
- package/docs/integrations/intercom.md +1 -1
- package/docs/integrations/linkedin-insights.md +1 -1
- package/docs/integrations/logrocket.md +1 -1
- package/docs/integrations/matomo-analytics.md +1 -1
- package/docs/integrations/meta-pixel.md +1 -1
- package/docs/integrations/microsoft-clarity.md +1 -1
- package/docs/integrations/microsoft-uet.md +1 -1
- package/docs/integrations/mixpanel-analytics.md +1 -1
- package/docs/integrations/one-dollar-stats.md +305 -0
- package/docs/integrations/openai-pixel.md +1 -1
- package/docs/integrations/overview.md +22 -18
- package/docs/integrations/pinterest-tag.md +321 -0
- package/docs/integrations/pirsch.md +1 -1
- package/docs/integrations/plausible-analytics.md +1 -1
- package/docs/integrations/posthog.md +1 -1
- package/docs/integrations/promptwatch.md +1 -1
- package/docs/integrations/reddit-pixel.md +1 -1
- package/docs/integrations/rudderstack.md +1 -1
- package/docs/integrations/rybbit-analytics.md +1 -1
- package/docs/integrations/segment.md +1 -1
- package/docs/integrations/snapchat-pixel.md +1 -1
- package/docs/integrations/tiktok-pixel.md +1 -1
- package/docs/integrations/umami-analytics.md +1 -1
- package/docs/integrations/vercel-analytics.md +1 -1
- package/docs/integrations/x-pixel.md +1 -1
- package/docs/integrations/youtube.md +27 -22
- package/docs/upgrade-v3.md +176 -1
- package/package.json +26 -16
- package/dist/component-hooks/use-manager.js +0 -2
- package/dist/components/frame/atoms.js +0 -1
- package/dist/components/frame/frame.js +0 -2
- package/dist/components/frame/index.js +0 -2
- package/dist/frame.js +0 -2
- package/dist-types/component-hooks/use-manager.d.ts +0 -40
- package/dist-types/components/frame/atoms.d.ts +0 -11
- package/dist-types/components/frame/frame.d.ts +0 -2
- package/dist-types/components/frame/index.d.ts +0 -24
- package/dist-types/frame.d.ts +0 -1
- package/readme.json +0 -48
- package/src/iab/styles.css +0 -11
- package/src/styles.css +0 -10
- /package/dist/components/{frame → consent-gate}/types.js +0 -0
|
@@ -93,18 +93,48 @@ it is `dialog`. These set that surface:
|
|
|
93
93
|
* The Customize button on a `ConsentBanner`, and the "Manage preferences"
|
|
94
94
|
or "Do not sell or share my data" button a notice renders.
|
|
95
95
|
* `ConsentDialogLink` and `ConsentDialogTrigger`.
|
|
96
|
-
* The button in a `
|
|
96
|
+
* The button in a `ConsentGate` placeholder.
|
|
97
97
|
* `useSetActiveUI()('dialog')` in your own component, or `openDialog()`
|
|
98
98
|
from `useHeadlessConsentUI()` on the headless subpath.
|
|
99
99
|
|
|
100
|
-
The component code is split into its own chunk
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
100
|
+
The component code is split into its own chunk, so it is not part of the
|
|
101
|
+
first page load. It renders nothing until that chunk is ready, and the
|
|
102
|
+
chunk downloads at the first of these:
|
|
103
|
+
|
|
104
|
+
* The browser's first idle period after the page's `load` event, while the
|
|
105
|
+
banner is shown or a `ConsentDialogLink`, `ConsentDialogTrigger`,
|
|
106
|
+
`ConsentGate` button or other button that opens the dialog is mounted.
|
|
107
|
+
This covers a visitor who taps or presses Enter with no hover or focus
|
|
108
|
+
first. Browsers without `requestIdleCallback`, such as Safari, wait
|
|
109
|
+
200 ms after `load` instead.
|
|
110
|
+
* Hover or focus on a button that opens the dialog.
|
|
111
|
+
* The dialog opening.
|
|
112
|
+
|
|
113
|
+
Passing `open={true}` or `showTrigger` loads it on mount. A visit with
|
|
114
|
+
saved consent and nothing on the page that opens the dialog never
|
|
115
|
+
downloads it.
|
|
116
|
+
|
|
117
|
+
Idle loading is skipped when the visitor has Save-Data on
|
|
118
|
+
(`navigator.connection.saveData`), on a `2g` or `slow-2g` connection, and
|
|
119
|
+
while offline; hover and focus still load it. To load the chunk only on
|
|
120
|
+
hover, focus or open, set `preloadDialog: 'intent'` in the provider
|
|
121
|
+
options. The default is `'idle'`. With Next.js, pass it through
|
|
122
|
+
`ConsentRoot`'s `options` prop.
|
|
123
|
+
|
|
124
|
+
If a preload fails, for example because the visitor is offline, nothing is
|
|
125
|
+
cached: the next hover, focus or open tries the download again.
|
|
126
|
+
|
|
127
|
+
The dialog's CSS travels in the same chunk and is applied before the dialog
|
|
128
|
+
renders, so your app's `styles.css` import stays small.
|
|
104
129
|
|
|
105
130
|
Without an `open` prop, Escape closes the dialog. Save, Accept all and
|
|
106
|
-
Reject all close it
|
|
107
|
-
|
|
131
|
+
Reject all close it as soon as the choice is recorded in the browser, in
|
|
132
|
+
the same task as the click; the exit animation still plays. The request to
|
|
133
|
+
the backend runs afterwards and never reopens the dialog: a failed request
|
|
134
|
+
keeps the choice, reports through `onError` and is retried later. See
|
|
135
|
+
[when a choice is saved](../../../guides/consent-state.md#when-a-choice-is-saved)
|
|
136
|
+
for the order of events. A draft that went stale because the policy changed keeps the dialog open
|
|
137
|
+
for review. With `open={true}`, these actions leave the dialog visible; the
|
|
108
138
|
parent must set `open={false}` to close it. Clicking the backdrop does not
|
|
109
139
|
close it. Closing discards toggles that were not saved: the next open starts
|
|
110
140
|
from the recorded choice again. After a save from an uncontrolled dialog,
|
|
@@ -186,6 +216,15 @@ for the stock structure are `dialog.root`, `dialog.container`,
|
|
|
186
216
|
`dialog.card`, `dialog.header`, `dialog.title`, `dialog.content`,
|
|
187
217
|
`dialog.overlay`, `description.dialog`, `manager.footer` and `tag.dialog`.
|
|
188
218
|
|
|
219
|
+
`ConsentDialog` from the package root and from the
|
|
220
|
+
`c15t/react/consent-dialog` subpath is the deferred component described
|
|
221
|
+
under Behavior, and each `ConsentDialog.<Part>` loads with the same chunk.
|
|
222
|
+
`c15t/react/components/consent-dialog` exports the dialog without the
|
|
223
|
+
deferral, plus each part as a named export (`Card`, `Header`, `Overlay`,
|
|
224
|
+
`Root` and the rest). Importing from that path puts the dialog's code in
|
|
225
|
+
the bundle of every page that imports it, so the first open needs no
|
|
226
|
+
download but every visitor downloads the dialog.
|
|
227
|
+
|
|
189
228
|
The compound parts import from `c15t/react` as properties of `ConsentDialog`,
|
|
190
229
|
and `ConsentWidget` from the same module. See
|
|
191
230
|
[Styling](../styling/overview.md) for slots and tokens.
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
---
|
|
2
|
-
title:
|
|
3
|
-
description: Gate a YouTube or map iframe behind consent with
|
|
4
|
-
showing a placeholder that opens the preference center until the
|
|
5
|
-
allowed.
|
|
2
|
+
title: ConsentGate
|
|
3
|
+
description: Gate a YouTube or map iframe behind consent with ConsentGate in
|
|
4
|
+
React, showing a placeholder that opens the preference center until the
|
|
5
|
+
category is allowed.
|
|
6
6
|
group: frameworks
|
|
7
7
|
---
|
|
8
8
|
|
|
9
9
|
## Gate an embed behind consent
|
|
10
10
|
|
|
11
|
-
`
|
|
11
|
+
`ConsentGate` mounts its children only while the effective permission for one
|
|
12
12
|
consent category is granted, and shows a placeholder otherwise. Wrap any
|
|
13
13
|
iframe or third-party widget that would set cookies or contact a vendor on
|
|
14
14
|
load. Render it anywhere inside the `ConsentProvider` from your
|
|
@@ -16,11 +16,11 @@ load. Render it anywhere inside the `ConsentProvider` from your
|
|
|
16
16
|
placeholder button opens.
|
|
17
17
|
|
|
18
18
|
```tsx title="src/product-video.tsx"
|
|
19
|
-
import {
|
|
19
|
+
import { ConsentGate } from 'c15t/react';
|
|
20
20
|
|
|
21
21
|
export function ProductVideo() {
|
|
22
22
|
return (
|
|
23
|
-
<
|
|
23
|
+
<ConsentGate category="marketing" className="video-frame">
|
|
24
24
|
<iframe
|
|
25
25
|
src="https://www.youtube-nocookie.com/embed/dQw4w9WgXcQ"
|
|
26
26
|
title="Product tour"
|
|
@@ -28,18 +28,18 @@ export function ProductVideo() {
|
|
|
28
28
|
allowFullScreen
|
|
29
29
|
style={{ width: '100%', aspectRatio: '16 / 9', border: 0 }}
|
|
30
30
|
/>
|
|
31
|
-
</
|
|
31
|
+
</ConsentGate>
|
|
32
32
|
);
|
|
33
33
|
}
|
|
34
34
|
```
|
|
35
35
|
|
|
36
36
|
`marketing` must be in your policy scope. If you set a non-empty
|
|
37
37
|
`options.consentCategories` list on `ConsentProvider`, include `marketing`
|
|
38
|
-
there too so the preference center can display and save it. `
|
|
38
|
+
there too so the preference center can display and save it. `ConsentGate` does
|
|
39
39
|
not add categories to that list.
|
|
40
40
|
|
|
41
41
|
The placeholder names the category using its title from your translations. Set `className` or `style`
|
|
42
|
-
on `
|
|
42
|
+
on `ConsentGate` to reserve the embed's space, so the page does not shift when the
|
|
43
43
|
placeholder is replaced.
|
|
44
44
|
|
|
45
45
|
## Props
|
|
@@ -51,22 +51,22 @@ placeholder is replaced.
|
|
|
51
51
|
| `placeholder` | `ReactNode` | built-in placeholder | Replaces the built-in title and button while the category is not allowed. Falsy values such as `null`, `false`, `0` and an empty string use the built-in placeholder. Pass an empty fragment, `<></>`, to render no placeholder content. |
|
|
52
52
|
|
|
53
53
|
Other `div` attributes such as `className`, `style` and `ref` apply to the
|
|
54
|
-
wrapper element that stays in the page in both states. `
|
|
54
|
+
wrapper element that stays in the page in both states. `ConsentGateProps` also
|
|
55
55
|
declares `noStyle` and `theme`, but the component does not apply them yet;
|
|
56
56
|
style the wrapper with `className` and theme the placeholder through the
|
|
57
57
|
provider `theme` option.
|
|
58
58
|
|
|
59
59
|
## Behavior
|
|
60
60
|
|
|
61
|
-
`
|
|
61
|
+
`ConsentGate` renders a `div` wrapper. Inside it, when the effective permission for
|
|
62
62
|
`category` is granted, it renders the children; otherwise it renders the
|
|
63
63
|
placeholder. Permission is the same value `useConsent(category)` returns,
|
|
64
64
|
so it can be granted under an opt-out rule before the visitor records a
|
|
65
65
|
choice, and a recorded grant can be overridden by a privacy signal.
|
|
66
66
|
|
|
67
67
|
While effective permission is denied, the children are absent from the DOM,
|
|
68
|
-
so an iframe or a third-party widget inside `
|
|
69
|
-
children unmount and the embed disappears. `
|
|
68
|
+
so an iframe or a third-party widget inside `ConsentGate` sends no requests. When the visitor later revokes it, the
|
|
69
|
+
children unmount and the embed disappears. `ConsentGate` does not depend on the
|
|
70
70
|
network blocker or the iframe blocker modules; use those for markup you
|
|
71
71
|
cannot wrap in a component.
|
|
72
72
|
|
|
@@ -84,18 +84,20 @@ scope cannot be granted. The built-in placeholder then shows `frame.policyBlocke
|
|
|
84
84
|
the button, and a stored grant for that category stays blocked.
|
|
85
85
|
|
|
86
86
|
When the provider starts from a snapshot prefetched on the server, the
|
|
87
|
-
server renders the
|
|
88
|
-
|
|
89
|
-
|
|
87
|
+
server renders the placeholder for a denied category. For a granted
|
|
88
|
+
category it renders the empty wrapper, and the children mount once
|
|
89
|
+
hydration completes, so an iframe loads once even when the page streams
|
|
90
|
+
inside a `Suspense` boundary. A grant in the request cookie allows the
|
|
91
|
+
embed only when policy and privacy signals permit it. Browser privacy signals detected during hydration can
|
|
90
92
|
withdraw that permission. In a browser-only setup the
|
|
91
93
|
provider has no permission until it resolves policy on the client, so
|
|
92
|
-
`
|
|
94
|
+
`ConsentGate` shows the placeholder first and swaps in the embed after resolution
|
|
93
95
|
when the category is already granted.
|
|
94
96
|
|
|
95
|
-
`
|
|
97
|
+
`ConsentGate` is one component on this page; the per-vendor embed guides for
|
|
96
98
|
[YouTube](../../../integrations/youtube.md) and
|
|
97
99
|
[Google Maps](../../../integrations/google-maps.md) show a complete embed
|
|
98
|
-
configuration with sizing, titles and the same `
|
|
100
|
+
configuration with sizing, titles and the same `ConsentGate` usage across
|
|
99
101
|
frameworks.
|
|
100
102
|
|
|
101
103
|
To read the same permission in your own components, use `useConsent` from
|
|
@@ -104,24 +106,24 @@ also import from there.
|
|
|
104
106
|
|
|
105
107
|
## Composition
|
|
106
108
|
|
|
107
|
-
The placeholder parts are available as `
|
|
108
|
-
`
|
|
109
|
-
behavior. `
|
|
109
|
+
The placeholder parts are available as `ConsentGate.Root`, `ConsentGate.Title` and
|
|
110
|
+
`ConsentGate.Button` for a custom placeholder that keeps the built-in copy and
|
|
111
|
+
behavior. `ConsentGate.Title` and `ConsentGate.Button` accept `category` and fill in the
|
|
110
112
|
translated text; pass children to either to replace it.
|
|
111
113
|
|
|
112
114
|
```tsx
|
|
113
|
-
<
|
|
115
|
+
<ConsentGate
|
|
114
116
|
category="marketing"
|
|
115
117
|
placeholder={
|
|
116
|
-
<
|
|
117
|
-
<
|
|
118
|
+
<ConsentGate.Root>
|
|
119
|
+
<ConsentGate.Title category="marketing" />
|
|
118
120
|
<p>The video is also available on our channel.</p>
|
|
119
|
-
<
|
|
120
|
-
</
|
|
121
|
+
<ConsentGate.Button category="marketing">Choose cookies</ConsentGate.Button>
|
|
122
|
+
</ConsentGate.Root>
|
|
121
123
|
}
|
|
122
124
|
>
|
|
123
125
|
<iframe src="https://www.youtube-nocookie.com/embed/..." title="Product tour" />
|
|
124
|
-
</
|
|
126
|
+
</ConsentGate>
|
|
125
127
|
```
|
|
126
128
|
|
|
127
129
|
The built-in placeholder carries `data-testid="frame-placeholder"` and its
|
|
@@ -131,7 +133,7 @@ button `data-testid="frame-open-dialog"`.
|
|
|
131
133
|
|
|
132
134
|
The placeholder is plain text and a `button`, so it is readable and
|
|
133
135
|
operable without the embed. Give the iframe a descriptive `title`, and keep a
|
|
134
|
-
transcript, address or link outside the `
|
|
136
|
+
transcript, address or link outside the `ConsentGate` for visitors who decline
|
|
135
137
|
the category. A denied category may be fixed by policy, so opening the
|
|
136
138
|
preference center does not guarantee that the visitor can grant it.
|
|
137
139
|
|
|
@@ -142,7 +144,9 @@ center and is not restricted by policy or privacy signals such as GPC.
|
|
|
142
144
|
Load the page with the category denied. The placeholder text names the
|
|
143
145
|
category and the network panel shows no request to the embed's host. With a
|
|
144
146
|
prefetched snapshot, the server HTML contains the placeholder for a denied
|
|
145
|
-
category
|
|
147
|
+
category and neither placeholder nor embed for a granted category; the
|
|
148
|
+
embed appears after hydration and the network panel shows one request for
|
|
149
|
+
its document. Browser-only initialization
|
|
146
150
|
shows the placeholder before policy resolves, then replaces it with the
|
|
147
151
|
embed if the category is granted. After policy resolves, activate the
|
|
148
152
|
placeholder button: the preference center opens. Turn the category on and
|
|
@@ -35,16 +35,18 @@ subscriptions. Keep it mounted across client navigation. Its initial mode and
|
|
|
35
35
|
prefetch configuration belong to the runtime's creation, not a per-render
|
|
36
36
|
mechanism for switching visitors or backends.
|
|
37
37
|
|
|
38
|
-
| Option
|
|
39
|
-
|
|
|
40
|
-
| `mode`
|
|
41
|
-
| `scripts`
|
|
42
|
-
| `callbacks`
|
|
43
|
-
| `theme`, `components`
|
|
44
|
-
| `presentation`
|
|
45
|
-
| `
|
|
46
|
-
| `
|
|
47
|
-
| `
|
|
38
|
+
| Option | Purpose |
|
|
39
|
+
| ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------- |
|
|
40
|
+
| `mode` | Hosted, offline or custom transport factory |
|
|
41
|
+
| `scripts` | Consent-managed script configurations |
|
|
42
|
+
| `callbacks` | Choice, permission and error events |
|
|
43
|
+
| `theme`, `components` | Consent-action styles, theme slots and component slot attributes. Tokens render through `ConsentTheme` |
|
|
44
|
+
| `presentation` | Prompt and preferences layout behavior |
|
|
45
|
+
| `preloadDialog` | When the deferred `ConsentDialog` starts loading: `'idle'` (default) or `'intent'`; see [ConsentDialog](./consent-dialog.md#behavior) |
|
|
46
|
+
| `i18n` | Languages and message overrides |
|
|
47
|
+
| `storageConfig` | Browser record storage configuration |
|
|
48
|
+
| `enabled` | Set false only when intentionally bypassing consent enforcement |
|
|
49
|
+
| `reloadOnConsentRevoked` | Reload the page after the visitor turns off a granted category or vendor; defaults to `true` |
|
|
48
50
|
|
|
49
51
|
`enabled: false` grants categories and allows gated loading while suppressing
|
|
50
52
|
consent UI. It is not a way to fix failed initialization or hide a banner in
|
|
@@ -58,9 +60,31 @@ signals. Hydration and notice dismissal do not become explicit choices.
|
|
|
58
60
|
|
|
59
61
|
An externally owned runtime can be passed through `runtime`. Its owner must
|
|
60
62
|
start and dispose it. A provider borrowing that runtime must not initialize or
|
|
61
|
-
dispose a second copy.
|
|
63
|
+
dispose a second copy. Passing a different `runtime` moves the provider and its
|
|
64
|
+
hooks to the new runtime's kernel and unsubscribes them from the previous one.
|
|
65
|
+
The previous runtime keeps running until its owner disposes it. Switch the
|
|
66
|
+
provider to another runtime, or unmount it, before disposing the runtime it
|
|
67
|
+
renders. Changing between a borrowed runtime and a provider-created one still
|
|
68
|
+
requires remounting the provider. IAB actions taken before its handle is ready
|
|
69
|
+
wait for that handle. If the provider unmounts or switches runtimes first, a
|
|
70
|
+
pending `save()` rejects with an `AbortError` instead of waiting indefinitely.
|
|
71
|
+
See [consent state](../../../guides/consent-state.md) and
|
|
62
72
|
[deployment modes](../../../guides/deployment-modes.md).
|
|
63
73
|
|
|
74
|
+
## Reload after revocation
|
|
75
|
+
|
|
76
|
+
Removing a script cannot stop code that already ran. A vendor's listeners,
|
|
77
|
+
timers and widgets keep working until the page unloads. When an accept, reject
|
|
78
|
+
or save turns off a category or vendor that was granted, the provider reloads
|
|
79
|
+
the page once the save request settles, so the next page runs only permitted
|
|
80
|
+
code. `onBeforeConsentRevocationReload` runs just before the reload. With a
|
|
81
|
+
[`consentSource`](../../../guides/shared-consent-controls.md), any withdrawal the
|
|
82
|
+
external CMP reports reloads the same way, including expiry.
|
|
83
|
+
|
|
84
|
+
Otherwise, expiry, policy changes and privacy signals do not reload the page. Set
|
|
85
|
+
`reloadOnConsentRevoked: false` only when every gated vendor stops itself on
|
|
86
|
+
revocation, for example through its own opt-out API.
|
|
87
|
+
|
|
64
88
|
## Clear data when consent is denied
|
|
65
89
|
|
|
66
90
|
Set `options.clearOnRevocation` to declare cookies, localStorage keys, and
|
|
@@ -85,6 +85,31 @@ Privacy Control overrides shows a note under its row: "Your saved choice is
|
|
|
85
85
|
restricted by the current privacy settings." The switch still reflects the
|
|
86
86
|
saved value; the effective permission is off.
|
|
87
87
|
|
|
88
|
+
### Vendors
|
|
89
|
+
|
|
90
|
+
When the provider declares `vendors`, each category's expanded description
|
|
91
|
+
ends with one card per vendor whose category condition names that category,
|
|
92
|
+
styled like a category row: the vendor's name and a switch on the header,
|
|
93
|
+
and its description and privacy policy link behind the card's own expand
|
|
94
|
+
control. Cards start collapsed, so a long list stays one line per vendor,
|
|
95
|
+
and the category list scrolls inside the dialog rather than growing it.
|
|
96
|
+
A collapsed category or card renders its content element empty and mounts
|
|
97
|
+
the content the first time it opens, then keeps it mounted, so a long
|
|
98
|
+
vendor list costs nothing until a visitor expands its category.
|
|
99
|
+
Vendor switches edit the same draft as the category
|
|
100
|
+
switches and record on Save. Saving with one vendor off denies that vendor
|
|
101
|
+
only; the category stays granted and its other vendors keep loading. While
|
|
102
|
+
the category is off in the draft the vendor switches are disabled, with the
|
|
103
|
+
hint "Turn on this category to choose vendors." A vendor declared
|
|
104
|
+
`disabled`, or one that only falls under `necessary`, is listed without a
|
|
105
|
+
switch. Accept all and Reject all clear every vendor denial, including one
|
|
106
|
+
staged but not yet saved. Under an `iab` policy no vendor rows render.
|
|
107
|
+
Vendors that are only referenced by a script slug, without a name and
|
|
108
|
+
privacy policy URL, still gate loading but are not listed, and so does a
|
|
109
|
+
vendor whose condition negates a category, since no category row can
|
|
110
|
+
host it. See
|
|
111
|
+
[granular consent](../../../integrations/granular-consent.md).
|
|
112
|
+
|
|
88
113
|
### Actions
|
|
89
114
|
|
|
90
115
|
The preference center always renders Reject all, Accept all and Save, with
|
|
@@ -102,8 +127,12 @@ such as `['disclosure']`, renders the widget and Save completes without
|
|
|
102
127
|
writing a consent record.
|
|
103
128
|
|
|
104
129
|
For a fully custom preference center, `useConsentDraft()` returns the same
|
|
105
|
-
draft: `values`, `displayedCategories`, `
|
|
106
|
-
`update`, `acceptAll`, `rejectAll`, `save` and `reset`.
|
|
130
|
+
draft: `values`, `displayedCategories`, `vendors`, `isDirty`, `isStale`,
|
|
131
|
+
`set`, `update`, `setVendor`, `acceptAll`, `rejectAll`, `save` and `reset`.
|
|
132
|
+
A custom vendor control alone takes `useVendorDraft()`, the vendor slice of
|
|
133
|
+
that draft: `vendors`, `setVendor`, `isDirty`, `isStale`, `save` and `reset`.
|
|
134
|
+
`useDeclaredVendors()`, `useVendorChoice()` and `useVendorAllowed(id)` read
|
|
135
|
+
the declared list, the recorded denials and one vendor's effective result.
|
|
107
136
|
|
|
108
137
|
`useConsentDraft` and `ConsentDraftProvider` import from `c15t/react`.
|
|
109
138
|
|
|
@@ -112,7 +141,10 @@ draft: `values`, `displayedCategories`, `isDirty`, `isStale`, `set`,
|
|
|
112
141
|
Each row is a disclosure: a button that expands the category description
|
|
113
142
|
and a switch beside it, so a visitor can read the description without
|
|
114
143
|
changing the choice. Each switch has the category title as its accessible
|
|
115
|
-
name.
|
|
144
|
+
name. A category's vendor cards sit in a `region` named "Vendors" with the
|
|
145
|
+
count; each card's expand button carries `aria-expanded`, each vendor
|
|
146
|
+
switch is named "Allow" followed by the vendor name, and is described by
|
|
147
|
+
the vendor's name element. The policy-change alert uses `role="alert"`, and a restriction note is
|
|
116
148
|
an `output` element linked to its switch through `aria-describedby`. The
|
|
117
149
|
root's `dir` attribute follows the active language.
|
|
118
150
|
|
|
@@ -122,7 +154,8 @@ Every part is available as `ConsentWidget.<Part>`. `Root` provides the
|
|
|
122
154
|
draft and accepts `noStyle`, `disableAnimation` and `uiSource`. `Accordion`
|
|
123
155
|
and `AccordionItems` render the stock rows; `AccordionItem`,
|
|
124
156
|
`AccordionTrigger`, `AccordionTriggerInner`, `AccordionContent`,
|
|
125
|
-
`AccordionArrow` and `Switch` build your own
|
|
157
|
+
`AccordionArrow` and `Switch` build your own; `VendorList` renders one
|
|
158
|
+
category's vendor rows and takes a `category` prop. `PolicyActions` renders the
|
|
126
159
|
resolved actions in `Footer` and `FooterSubGroup` and accepts `renderAction`
|
|
127
160
|
to replace one button; `AcceptAllButton`, `RejectButton`, `SaveButton` and
|
|
128
161
|
`CustomizeButton` are the individual buttons.
|
|
@@ -155,8 +188,17 @@ export function PreferenceCenter() {
|
|
|
155
188
|
Provider component slots for the stock structure are `manager.root`,
|
|
156
189
|
`manager.footer`, `manager.actionGroup`, `accordion.root`,
|
|
157
190
|
`accordion.triggerRow`, `accordion.title`, `accordion.control`,
|
|
158
|
-
`accordion-item.root`, `accordion-item.trigger`, `accordion-item.content
|
|
159
|
-
|
|
191
|
+
`accordion-item.root`, `accordion-item.trigger`, `accordion-item.content`,
|
|
192
|
+
`vendor-list.root`, `vendor-list.trigger`, `vendor-list.content`,
|
|
193
|
+
`vendor-list.item`, `vendor-list.header`,
|
|
194
|
+
`vendor-list.name`, `vendor-list.description`, `vendor-list.link`,
|
|
195
|
+
`vendor-list.control` and `tag.manager`.
|
|
196
|
+
|
|
197
|
+
`ConsentWidget` from the package root and from the
|
|
198
|
+
`c15t/react/consent-widget` subpath loads its code in a separate chunk the
|
|
199
|
+
first time it renders, so a page that never renders it does not download
|
|
200
|
+
it. `c15t/react/components/consent-widget` exports the widget without that
|
|
201
|
+
deferral, plus each part as a named export.
|
|
160
202
|
|
|
161
203
|
The compound parts import from `c15t/react` as properties of `ConsentWidget`.
|
|
162
204
|
See [Styling](../styling/overview.md) for slots and tokens.
|
|
@@ -17,8 +17,23 @@ group: frameworks
|
|
|
17
17
|
|
|
18
18
|
`necessary` is always permitted. Assign categories based on what an integration
|
|
19
19
|
does in your application; renaming analytics to necessary does not change its
|
|
20
|
-
purpose.
|
|
21
|
-
|
|
20
|
+
purpose.
|
|
21
|
+
|
|
22
|
+
c15t discovers categories from registered scripts, network rules, React `ConsentGate`
|
|
23
|
+
components, and iframes with `data-category` handled by the iframe blocker.
|
|
24
|
+
The browser client also discovers inert scripts tagged with `data-c15t-category`.
|
|
25
|
+
Compound script and network conditions contribute every category they reference.
|
|
26
|
+
|
|
27
|
+
Discovered categories are added to `consentCategories`, when supplied. The dialog
|
|
28
|
+
always includes Necessary and offers the optional categories in that combined
|
|
29
|
+
set that are also in the resolved policy scope. Choice completion uses the same
|
|
30
|
+
set. If neither configuration nor integrations supply categories, the dialog
|
|
31
|
+
uses the full policy scope.
|
|
32
|
+
|
|
33
|
+
Categories discovered later are added immediately and retained until the provider
|
|
34
|
+
or runtime is recreated. Adding a category can require a new choice; removing a
|
|
35
|
+
script or unmounting a frame does not remove its category or erase consent.
|
|
36
|
+
Discovery does not grant consent or change backend permission restrictions.
|
|
22
37
|
|
|
23
38
|
## Respect policy scope
|
|
24
39
|
|
|
@@ -28,6 +43,27 @@ only some optional categories, set `scopeMode` explicitly. An omitted scope,
|
|
|
28
43
|
`['*']`, or a list containing only `necessary` expands to the default optional
|
|
29
44
|
categories; a necessary-only list is not a shortcut for disabling all tracking.
|
|
30
45
|
|
|
46
|
+
For example, a backend rule with `categories: ['necessary']` and scripts assigned
|
|
47
|
+
to `marketing` and `measurement` displays Necessary, Marketing, and Analytics,
|
|
48
|
+
even without `consentCategories`. Accept All records both optional choices and
|
|
49
|
+
keeps the banner dismissed after reload. An explicit list of
|
|
50
|
+
`['necessary', 'measurement']` with no other integrations displays Necessary and
|
|
51
|
+
Analytics.
|
|
52
|
+
|
|
53
|
+
Hidden optional categories do not need a choice to dismiss the banner. Their
|
|
54
|
+
permissions still follow the backend policy, so hidden opt-in categories remain
|
|
55
|
+
denied without a valid grant. React providers enable DOM iframe blocking and
|
|
56
|
+
discovery by default. The blocker loads when the first iframe with
|
|
57
|
+
`data-category` or `data-vendor` is on the page, so pages without one never
|
|
58
|
+
download it; until it runs, such an iframe that arrives with a `src` consent
|
|
59
|
+
does not allow is paused. Write gated iframes with `data-src`, not `src`. The
|
|
60
|
+
browser requests a `src` as soon as the iframe is in the document, before the
|
|
61
|
+
blocker can pause it, whether the iframe comes from the server HTML or a
|
|
62
|
+
client render. Set
|
|
63
|
+
`iframeBlocker: false` to disable it, or
|
|
64
|
+
`iframeBlocker: { disableAutomaticBlocking: true }` to scan manually with
|
|
65
|
+
`useIframeBlocker({ disableAutomaticBlocking: true }).processAllIframes()`.
|
|
66
|
+
|
|
31
67
|
Use effective permissions to gate work and explicit choices to inspect what the
|
|
32
68
|
visitor confirmed. Read [consent state](../../../guides/consent-state.md) for that
|
|
33
69
|
distinction and [migration](../../../upgrade-v3.md) for v3 policy configuration.
|
|
@@ -51,8 +51,10 @@ This is an isolated action example, not a complete policy-aware banner. Use the
|
|
|
51
51
|
stock UI or headless surface actions for a complete prompt. Do not call save on
|
|
52
52
|
mount to persist a state derived from permissions.
|
|
53
53
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
of
|
|
54
|
+
Use focused hooks for feature gates. For a complete custom prompt or preference
|
|
55
|
+
center, use `useHeadlessConsentUI()` for surface actions and `useConsentDraft()`
|
|
56
|
+
for staged choices, inside one `ConsentDraftProvider` so the save commits the
|
|
57
|
+
same draft. v3 has no `useConsentManager()`; the
|
|
58
|
+
[upgrade guide](../../../upgrade-v3.md#replace-useconsentmanager) maps each of its
|
|
59
|
+
fields to a hook. Read [consent state](../../../guides/consent-state.md) before
|
|
60
|
+
treating a callback as evidence of a choice.
|
|
@@ -16,7 +16,9 @@ components inside an existing consent provider connected to Inth.
|
|
|
16
16
|
|
|
17
17
|
Import the components from `c15t/react/iab` and render them inside your
|
|
18
18
|
existing `ConsentProvider` from `c15t/react`. Import `c15t/react/iab/styles.css`
|
|
19
|
-
at your global stylesheet entry.
|
|
19
|
+
at your global stylesheet entry, after `c15t/react/styles.css`: the IAB
|
|
20
|
+
stylesheet holds only the IAB rules and uses the tokens from the base
|
|
21
|
+
stylesheet.
|
|
20
22
|
|
|
21
23
|
```tsx title="src/iab-consent.tsx"
|
|
22
24
|
import { IABProvider, IABConsentBanner, IABConsentDialog } from 'c15t/react/iab';
|
|
@@ -51,3 +53,121 @@ Test category choices alongside vendor, purpose, legitimate-interest and special
|
|
|
51
53
|
feature controls used by your integration. Confirm the TC String and `__tcfapi`
|
|
52
54
|
reflect the saved state, and verify the vendor's actual requests. A category-only
|
|
53
55
|
banner test does not establish that the IAB flow works.
|
|
56
|
+
|
|
57
|
+
## Restrict how vendors use a purpose
|
|
58
|
+
|
|
59
|
+
Publisher restrictions override a vendor's declared use of one purpose. Pass
|
|
60
|
+
them to `IABProvider` as `publisherRestrictions`. The vendor IDs below are
|
|
61
|
+
placeholders; use vendors from your list whose declarations allow each
|
|
62
|
+
restriction.
|
|
63
|
+
|
|
64
|
+
```tsx title="src/iab-consent.tsx"
|
|
65
|
+
import { IABProvider, IABConsentBanner, IABConsentDialog } from 'c15t/react/iab';
|
|
66
|
+
|
|
67
|
+
const publisherRestrictions = [
|
|
68
|
+
// Vendors 10 and 755 may not use purpose 4.
|
|
69
|
+
{ purposeId: 4, restrictionType: 0 as const, vendorIds: [10, 755] },
|
|
70
|
+
// Vendor 755 must use legitimate interest for purpose 7.
|
|
71
|
+
{ purposeId: 7, restrictionType: 2 as const, vendorIds: [755] },
|
|
72
|
+
];
|
|
73
|
+
|
|
74
|
+
export function IABConsent({ cmpId }: { cmpId: number }) {
|
|
75
|
+
return (
|
|
76
|
+
<IABProvider cmpId={cmpId} publisherRestrictions={publisherRestrictions}>
|
|
77
|
+
<IABConsentBanner />
|
|
78
|
+
<IABConsentDialog />
|
|
79
|
+
</IABProvider>
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Each restriction type has a requirement that depends on the vendor list:
|
|
85
|
+
|
|
86
|
+
| `restrictionType` | Meaning | Allowed when the vendor |
|
|
87
|
+
| ----------------- | -------------------------------------- | ------------------------------------------------------------------------------- |
|
|
88
|
+
| `0` | Purpose not allowed on any legal basis | declares the purpose for consent or legitimate interest |
|
|
89
|
+
| `1` | Consent required | declares the purpose for legitimate interest and lists it in `flexiblePurposes` |
|
|
90
|
+
| `2` | Legitimate interest required | declares the purpose for consent and lists it in `flexiblePurposes` |
|
|
91
|
+
|
|
92
|
+
Type `2` is never allowed for purposes 1, 3, 4, 5 and 6, which TCF allows
|
|
93
|
+
only with consent. Restrictions also require a service-specific CMP, the
|
|
94
|
+
default. Setting `isServiceSpecific: false` with restrictions is an error.
|
|
95
|
+
c15t copies the restrictions when the CMP starts, so changing your array
|
|
96
|
+
afterwards has no effect.
|
|
97
|
+
|
|
98
|
+
c15t checks restrictions when the vendor list loads and again before each TC
|
|
99
|
+
String is encoded. Unsupported restrictions include type `3`, which the spec
|
|
100
|
+
reserves, a vendor or purpose missing from the list, and a vendor with two
|
|
101
|
+
restriction types for one purpose. They are never dropped. Loading IAB
|
|
102
|
+
settings, saving and generating a TC String all reject with a
|
|
103
|
+
`PublisherRestrictionError` exported by `@c15t/iab`. On a `createIAB` handle
|
|
104
|
+
these are `whenReady()`, `save()` and `generateTCString()`. No TC String is
|
|
105
|
+
written and no consent is recorded. Retrying `whenReady()` does not fetch
|
|
106
|
+
another vendor list. The error lasts until a new vendor list is set up: with an
|
|
107
|
+
explicit `gvl` option that never happens, so saving keeps failing even if the
|
|
108
|
+
kernel later holds a different list. When the CMP follows the kernel's list
|
|
109
|
+
instead, a replacement list is checked again and saving works once it accepts
|
|
110
|
+
the restrictions. When a replacement vendor list makes a restriction
|
|
111
|
+
unsupported, c15t also clears the TC authority confirmed under the previous
|
|
112
|
+
list, so gated scripts stop until the configuration is fixed. Whenever c15t
|
|
113
|
+
withdraws the authority it confirmed, it also removes the TC String from the
|
|
114
|
+
`euconsent-v2` cookie and localStorage entry. Because the check reads the
|
|
115
|
+
current vendor list, a vendor that stops declaring a purpose or its flexibility
|
|
116
|
+
makes a restriction invalid. Narrow a `vendors` allowlist and your restrictions
|
|
117
|
+
together.
|
|
118
|
+
|
|
119
|
+
Saved TC Strings carry the restrictions in their `PubRestrictions` section, and
|
|
120
|
+
`__tcfapi('getTCData')` reports them as `publisher.restrictions`, keyed by
|
|
121
|
+
purpose ID, then vendor ID. A stored TC String whose restrictions differ from
|
|
122
|
+
the current configuration is not restored. If the visitor's stored choice is
|
|
123
|
+
otherwise current, the banner opens again, as it does after a policy change,
|
|
124
|
+
and closes once the visitor saves. IAB gates that depend on the TC String stay
|
|
125
|
+
denied until then. c15t also removes the superseded TC String from the
|
|
126
|
+
`euconsent-v2` cookie and localStorage entry, so vendors reading standard
|
|
127
|
+
storage do not pick it up. An unchanged configuration restores the stored TC
|
|
128
|
+
String without asking.
|
|
129
|
+
When reading a string written under TCF policy version 2 or 3, c15t accepts
|
|
130
|
+
type `2` for purposes 3 to 6, which those versions allowed. Strings c15t
|
|
131
|
+
writes always follow the current policy.
|
|
132
|
+
|
|
133
|
+
The React, Vue, Svelte and `@c15t/browser/iab` preference centres list each
|
|
134
|
+
vendor under the legal basis the restrictions leave it. A vendor whose purpose
|
|
135
|
+
now requires legitimate interest gets an objection control instead of a
|
|
136
|
+
consent toggle, and a prohibited purpose no longer lists the vendor. A
|
|
137
|
+
purpose whose vendors all use legitimate interest has no consent switch,
|
|
138
|
+
because turning one off would change nothing the vendors rely on; its
|
|
139
|
+
objection control is the opt-out. Such a purpose does not decide its c15t
|
|
140
|
+
category: the category follows the purposes in it that the visitor can
|
|
141
|
+
consent to. Legitimate interest never grants a category on its own. If none of
|
|
142
|
+
a category's purposes has a consent basis, the category needs all of them
|
|
143
|
+
consented, which only Accept all does. Stack switches
|
|
144
|
+
cover only the purposes some vendor processes on consent. Display-model rows
|
|
145
|
+
expose this as `hasConsentBasis`. Custom preference UIs can call
|
|
146
|
+
`applyPublisherRestrictionsToGVL` from `@c15t/iab/headless`, or pass
|
|
147
|
+
`publisherRestrictions` to `processGVLForDialog`, to get the same vendor
|
|
148
|
+
declarations. The configured restrictions are on the kernel's IAB state as
|
|
149
|
+
`publisherRestrictions`.
|
|
150
|
+
|
|
151
|
+
Consent-gated scripts, network rules and iframes with a `vendorId` also apply
|
|
152
|
+
the confirmed restrictions for that vendor:
|
|
153
|
+
|
|
154
|
+
* Type `0` blocks a target that declares the purpose in `iabPurposes` or
|
|
155
|
+
`iabLegIntPurposes`.
|
|
156
|
+
* Type `1` makes a purpose declared in `iabLegIntPurposes` require purpose and
|
|
157
|
+
vendor consent. Legitimate interest no longer satisfies it.
|
|
158
|
+
* Type `2` makes a purpose declared in `iabPurposes` require purpose and vendor
|
|
159
|
+
legitimate interest. Consent no longer satisfies it.
|
|
160
|
+
|
|
161
|
+
Types `1` and `2` block the target when the vendor list does not mark the
|
|
162
|
+
purpose as flexible for that vendor. Targets without a `vendorId` ignore
|
|
163
|
+
restrictions. A target that uses only legitimate interest once restrictions
|
|
164
|
+
apply is not blocked by a refused category, since it needs no consent; its
|
|
165
|
+
legitimate interest signals and the visitor's objection decide. GPC, opt-out
|
|
166
|
+
directives and strict scope still block it, and the category stays refused for
|
|
167
|
+
scripts that depend on it. Accept all grants the vendor signal a restriction
|
|
168
|
+
moves a purpose to, including legitimate interest for a vendor that declares
|
|
169
|
+
none. Legitimate interest a restriction introduces applies until the visitor
|
|
170
|
+
objects: the preference centres show it as allowed, and saving without touching
|
|
171
|
+
it encodes it as allowed. `introducedLegitimateInterest` from
|
|
172
|
+
`@c15t/iab/headless` lists those purposes and vendors for custom preference
|
|
173
|
+
UIs.
|