@c15t/scripts 2.2.0 → 3.0.0-alpha.1

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.
Files changed (202) hide show
  1. package/AGENTS.md +77 -48
  2. package/README.md +4 -3
  3. package/dist/e2e-test-utils.js +60 -24
  4. package/dist/engine/compile.js +45 -45
  5. package/dist/engine/runtime.js +130 -119
  6. package/dist/registry.js +196 -176
  7. package/dist/resolve.js +12 -12
  8. package/dist/vendors/_shared/attributes.js +5 -5
  9. package/dist/vendors/_shared/google-consent.js +10 -10
  10. package/dist/vendors/_shared/install-builders.js +9 -9
  11. package/dist/vendors/_shared/script-url.js +12 -12
  12. package/dist/vendors/ads-and-pixels/linkedin-insights.js +16 -16
  13. package/dist/vendors/ads-and-pixels/meta-pixel.js +82 -82
  14. package/dist/vendors/ads-and-pixels/microsoft-uet.js +57 -57
  15. package/dist/vendors/ads-and-pixels/openai-pixel.js +88 -0
  16. package/dist/vendors/ads-and-pixels/reddit-pixel.js +39 -39
  17. package/dist/vendors/ads-and-pixels/snapchat-pixel.js +25 -25
  18. package/dist/vendors/ads-and-pixels/tiktok-pixel.js +31 -31
  19. package/dist/vendors/ads-and-pixels/x-pixel.js +17 -17
  20. package/dist/vendors/analytics/adobe-analytics.js +17 -17
  21. package/dist/vendors/analytics/ahrefs-analytics.js +8 -8
  22. package/dist/vendors/analytics/amplitude.js +39 -39
  23. package/dist/vendors/analytics/clearbit.js +11 -11
  24. package/dist/vendors/analytics/cloudflare-web-analytics.js +13 -13
  25. package/dist/vendors/analytics/databuddy.js +45 -45
  26. package/dist/vendors/analytics/fathom-analytics.js +15 -15
  27. package/dist/vendors/analytics/google-tag.js +23 -23
  28. package/dist/vendors/analytics/heap.js +37 -37
  29. package/dist/vendors/analytics/hightouch.js +30 -30
  30. package/dist/vendors/analytics/hotjar.js +14 -14
  31. package/dist/vendors/analytics/logrocket.js +24 -24
  32. package/dist/vendors/analytics/matomo-analytics.js +51 -51
  33. package/dist/vendors/analytics/microsoft-clarity.js +34 -31
  34. package/dist/vendors/analytics/mixpanel-analytics.js +31 -31
  35. package/dist/vendors/analytics/pirsch.js +27 -27
  36. package/dist/vendors/analytics/plausible-analytics.js +24 -24
  37. package/dist/vendors/analytics/posthog.js +84 -79
  38. package/dist/vendors/analytics/promptwatch.js +8 -8
  39. package/dist/vendors/analytics/rudderstack.js +50 -50
  40. package/dist/vendors/analytics/rybbit-analytics.js +30 -30
  41. package/dist/vendors/analytics/segment.js +16 -16
  42. package/dist/vendors/analytics/umami-analytics.js +16 -16
  43. package/dist/vendors/analytics/vercel-analytics.js +22 -22
  44. package/dist/vendors/functional/crisp.js +49 -51
  45. package/dist/vendors/functional/intercom.js +18 -18
  46. package/dist/vendors/tag-managers/cloudflare-zaraz.js +98 -0
  47. package/dist/vendors/tag-managers/google-tag-manager.js +20 -20
  48. package/dist-types/__tests__/helpers.d.ts +11 -11
  49. package/dist-types/engine/compile.d.ts +3 -3
  50. package/dist-types/engine/runtime.d.ts +3 -3
  51. package/dist-types/registry.d.ts +191 -173
  52. package/dist-types/resolve.d.ts +3 -3
  53. package/dist-types/types.d.ts +2 -2
  54. package/dist-types/vendors/_shared/attributes.d.ts +2 -2
  55. package/dist-types/vendors/_shared/google-consent.d.ts +2 -2
  56. package/dist-types/vendors/_shared/install-builders.d.ts +2 -2
  57. package/dist-types/vendors/_shared/script-url.d.ts +6 -6
  58. package/dist-types/vendors/ads-and-pixels/linkedin-insights.d.ts +14 -14
  59. package/dist-types/vendors/ads-and-pixels/meta-pixel.d.ts +27 -27
  60. package/dist-types/vendors/ads-and-pixels/microsoft-uet.d.ts +40 -40
  61. package/dist-types/vendors/ads-and-pixels/openai-pixel.d.ts +211 -0
  62. package/dist-types/vendors/ads-and-pixels/reddit-pixel.d.ts +28 -29
  63. package/dist-types/vendors/ads-and-pixels/snapchat-pixel.d.ts +23 -23
  64. package/dist-types/vendors/ads-and-pixels/tiktok-pixel.d.ts +22 -22
  65. package/dist-types/vendors/ads-and-pixels/x-pixel.d.ts +15 -15
  66. package/dist-types/vendors/analytics/adobe-analytics.d.ts +3 -3
  67. package/dist-types/vendors/analytics/ahrefs-analytics.d.ts +5 -5
  68. package/dist-types/vendors/analytics/amplitude.d.ts +24 -24
  69. package/dist-types/vendors/analytics/clearbit.d.ts +5 -5
  70. package/dist-types/vendors/analytics/cloudflare-web-analytics.d.ts +6 -6
  71. package/dist-types/vendors/analytics/databuddy.d.ts +34 -31
  72. package/dist-types/vendors/analytics/fathom-analytics.d.ts +8 -8
  73. package/dist-types/vendors/analytics/google-tag.d.ts +17 -17
  74. package/dist-types/vendors/analytics/heap.d.ts +17 -17
  75. package/dist-types/vendors/analytics/hightouch.d.ts +15 -15
  76. package/dist-types/vendors/analytics/hotjar.d.ts +9 -9
  77. package/dist-types/vendors/analytics/logrocket.d.ts +11 -11
  78. package/dist-types/vendors/analytics/matomo-analytics.d.ts +3 -3
  79. package/dist-types/vendors/analytics/microsoft-clarity.d.ts +12 -13
  80. package/dist-types/vendors/analytics/mixpanel-analytics.d.ts +20 -20
  81. package/dist-types/vendors/analytics/pirsch.d.ts +10 -10
  82. package/dist-types/vendors/analytics/plausible-analytics.d.ts +13 -13
  83. package/dist-types/vendors/analytics/posthog.d.ts +35 -32
  84. package/dist-types/vendors/analytics/promptwatch.d.ts +5 -5
  85. package/dist-types/vendors/analytics/rudderstack.d.ts +16 -16
  86. package/dist-types/vendors/analytics/rybbit-analytics.d.ts +15 -15
  87. package/dist-types/vendors/analytics/segment.d.ts +11 -11
  88. package/dist-types/vendors/analytics/umami-analytics.d.ts +9 -9
  89. package/dist-types/vendors/analytics/vercel-analytics.d.ts +13 -13
  90. package/dist-types/vendors/functional/crisp.d.ts +9 -9
  91. package/dist-types/vendors/functional/intercom.d.ts +12 -12
  92. package/dist-types/vendors/tag-managers/cloudflare-zaraz.d.ts +39 -0
  93. package/dist-types/vendors/tag-managers/google-tag-manager.d.ts +16 -16
  94. package/docs/README.md +77 -48
  95. package/docs/assets/v3/brand-bar.png +0 -0
  96. package/docs/assets/v3/brand-card.png +0 -0
  97. package/docs/assets/v3/choice-wall.png +0 -0
  98. package/docs/assets/v3/mobile-card.png +0 -0
  99. package/docs/assets/v3/preferences.png +0 -0
  100. package/docs/customization/overview.md +45 -0
  101. package/docs/customization/recipes.md +79 -0
  102. package/docs/customization/slots.md +55 -0
  103. package/docs/customization/tokens.md +76 -0
  104. package/docs/customization/translations.md +49 -0
  105. package/docs/frameworks/javascript/script-loader.md +81 -343
  106. package/docs/frameworks/next/script-loader.md +164 -455
  107. package/docs/frameworks/react/script-loader.md +41 -533
  108. package/docs/guides/consent-state.md +60 -0
  109. package/docs/guides/data-fetching.md +163 -0
  110. package/docs/guides/deployment-modes.md +75 -0
  111. package/docs/guides/troubleshooting.md +68 -0
  112. package/docs/guides/verify-consent.md +62 -0
  113. package/docs/integrations/adobe-analytics.md +239 -105
  114. package/docs/integrations/ahrefs-analytics.md +238 -104
  115. package/docs/integrations/amplitude.md +219 -157
  116. package/docs/integrations/building-integrations.md +36 -223
  117. package/docs/integrations/clear-on-revocation.md +167 -0
  118. package/docs/integrations/clearbit.md +247 -86
  119. package/docs/integrations/cloudflare-web-analytics.md +250 -84
  120. package/docs/integrations/cloudflare-zaraz.md +399 -0
  121. package/docs/integrations/crisp.md +251 -97
  122. package/docs/integrations/databuddy.md +259 -153
  123. package/docs/integrations/fathom-analytics.md +239 -96
  124. package/docs/integrations/google-maps.md +328 -207
  125. package/docs/integrations/google-tag-manager.md +248 -96
  126. package/docs/integrations/google-tag.md +261 -90
  127. package/docs/integrations/granular-consent.md +208 -0
  128. package/docs/integrations/heap.md +222 -149
  129. package/docs/integrations/hightouch.md +225 -131
  130. package/docs/integrations/hotjar.md +239 -90
  131. package/docs/integrations/intercom.md +239 -98
  132. package/docs/integrations/linkedin-insights.md +243 -113
  133. package/docs/integrations/logrocket.md +241 -123
  134. package/docs/integrations/matomo-analytics.md +256 -111
  135. package/docs/integrations/meta-pixel.md +197 -324
  136. package/docs/integrations/microsoft-clarity.md +233 -114
  137. package/docs/integrations/microsoft-uet.md +245 -110
  138. package/docs/integrations/mixpanel-analytics.md +252 -87
  139. package/docs/integrations/openai-pixel.md +441 -0
  140. package/docs/integrations/overview.md +96 -133
  141. package/docs/integrations/pirsch.md +249 -96
  142. package/docs/integrations/plausible-analytics.md +241 -100
  143. package/docs/integrations/posthog.md +353 -214
  144. package/docs/integrations/promptwatch.md +251 -81
  145. package/docs/integrations/reddit-pixel.md +226 -173
  146. package/docs/integrations/rudderstack.md +244 -187
  147. package/docs/integrations/rybbit-analytics.md +244 -91
  148. package/docs/integrations/segment.md +238 -92
  149. package/docs/integrations/snapchat-pixel.md +240 -110
  150. package/docs/integrations/tiktok-pixel.md +249 -81
  151. package/docs/integrations/umami-analytics.md +242 -95
  152. package/docs/integrations/vercel-analytics.md +242 -90
  153. package/docs/integrations/x-pixel.md +238 -104
  154. package/docs/integrations/youtube.md +359 -142
  155. package/docs/upgrade-v3.md +381 -0
  156. package/package.json +90 -78
  157. package/dist/e2e-test-utils.cjs +0 -166
  158. package/dist/engine/compile.cjs +0 -130
  159. package/dist/engine/runtime.cjs +0 -475
  160. package/dist/registry.cjs +0 -423
  161. package/dist/resolve.cjs +0 -71
  162. package/dist/types.cjs +0 -69
  163. package/dist/vendors/_shared/attributes.cjs +0 -55
  164. package/dist/vendors/_shared/google-consent.cjs +0 -69
  165. package/dist/vendors/_shared/install-builders.cjs +0 -59
  166. package/dist/vendors/_shared/script-url.cjs +0 -78
  167. package/dist/vendors/ads-and-pixels/linkedin-insights.cjs +0 -89
  168. package/dist/vendors/ads-and-pixels/meta-pixel.cjs +0 -206
  169. package/dist/vendors/ads-and-pixels/microsoft-uet.cjs +0 -151
  170. package/dist/vendors/ads-and-pixels/reddit-pixel.cjs +0 -151
  171. package/dist/vendors/ads-and-pixels/snapchat-pixel.cjs +0 -131
  172. package/dist/vendors/ads-and-pixels/tiktok-pixel.cjs +0 -130
  173. package/dist/vendors/ads-and-pixels/x-pixel.cjs +0 -92
  174. package/dist/vendors/analytics/adobe-analytics.cjs +0 -90
  175. package/dist/vendors/analytics/ahrefs-analytics.cjs +0 -68
  176. package/dist/vendors/analytics/amplitude.cjs +0 -193
  177. package/dist/vendors/analytics/clearbit.cjs +0 -69
  178. package/dist/vendors/analytics/cloudflare-web-analytics.cjs +0 -73
  179. package/dist/vendors/analytics/databuddy.cjs +0 -144
  180. package/dist/vendors/analytics/fathom-analytics.cjs +0 -76
  181. package/dist/vendors/analytics/google-tag.cjs +0 -107
  182. package/dist/vendors/analytics/heap.cjs +0 -181
  183. package/dist/vendors/analytics/hightouch.cjs +0 -153
  184. package/dist/vendors/analytics/hotjar.cjs +0 -85
  185. package/dist/vendors/analytics/logrocket.cjs +0 -99
  186. package/dist/vendors/analytics/matomo-analytics.cjs +0 -232
  187. package/dist/vendors/analytics/microsoft-clarity.cjs +0 -138
  188. package/dist/vendors/analytics/mixpanel-analytics.cjs +0 -134
  189. package/dist/vendors/analytics/pirsch.cjs +0 -108
  190. package/dist/vendors/analytics/plausible-analytics.cjs +0 -122
  191. package/dist/vendors/analytics/posthog.cjs +0 -236
  192. package/dist/vendors/analytics/promptwatch.cjs +0 -70
  193. package/dist/vendors/analytics/rudderstack.cjs +0 -227
  194. package/dist/vendors/analytics/rybbit-analytics.cjs +0 -104
  195. package/dist/vendors/analytics/segment.cjs +0 -97
  196. package/dist/vendors/analytics/umami-analytics.cjs +0 -80
  197. package/dist/vendors/analytics/vercel-analytics.cjs +0 -94
  198. package/dist/vendors/functional/crisp.cjs +0 -143
  199. package/dist/vendors/functional/intercom.cjs +0 -89
  200. package/dist/vendors/tag-managers/google-tag-manager.cjs +0 -100
  201. package/docs/shared/react/guides/script-loader.md +0 -311
  202. package/readme.json +0 -19
@@ -0,0 +1,381 @@
1
+ ---
2
+ title: Upgrade to v3 policies
3
+ description: Migrate policy configuration, consent records, callbacks, and
4
+ custom transports to the v3 policy system.
5
+ group: reference
6
+ ---
7
+
8
+ ## React version requirement
9
+
10
+ The v3 React and Next.js adapters require React and React DOM 18 or newer.
11
+ Upgrade both packages together before installing v3. Earlier alpha peer ranges
12
+ incorrectly allowed React 16 and 17 even though v3 already uses `useId` and
13
+ `useSyncExternalStore`, which those versions do not provide.
14
+
15
+ ## Start with a policy rule
16
+
17
+ ```ts
18
+ import { policyRulePresets } from '@c15t/schema';
19
+ import { offline } from '@c15t/react';
20
+
21
+ const mode = offline({
22
+ policyRules: [policyRulePresets.europeOptIn()],
23
+ });
24
+ ```
25
+
26
+ Pass `mode` to `ConsentProvider`. For a backend integration, configure
27
+ `policyRules` on the backend and use `hosted({ url })` in the provider. Follow the
28
+ [React quickstart](https://c15t.com/docs/frameworks/react/quickstart),
29
+ [Next.js quickstart](https://c15t.com/docs/frameworks/next/quickstart), or
30
+ [JavaScript quickstart](https://c15t.com/docs/frameworks/javascript/quickstart) for a complete
31
+ integration.
32
+
33
+ The CLI no longer offers `offline-add-policy-packs`, which generated v2
34
+ configuration. For offline integrations, configure `offline({ policyRules })`
35
+ as shown above.
36
+
37
+ Replace legacy `policyPacks` and nested `consent` configuration with rules:
38
+
39
+ ```ts
40
+ import type { PolicyRule } from '@c15t/schema';
41
+
42
+ const policyRules = [
43
+ {
44
+ id: 'default-opt-in',
45
+ match: { fallback: true },
46
+ model: 'opt-in',
47
+ prompt: 'choice',
48
+ categories: ['measurement', 'marketing'],
49
+ scopeMode: 'strict',
50
+ },
51
+ ] satisfies PolicyRule[];
52
+ ```
53
+
54
+ `model` controls permission defaults. `prompt` controls whether the visitor must
55
+ make a choice, dismiss a notice, or see no prompt. Opt-in and IAB models require
56
+ `prompt: 'choice'`. Opt-out supports `choice`, `notice`, and `none`. The `none`
57
+ model permits optional categories in scope, allows only `prompt: 'none'`, owes
58
+ no rights, and renders no consent UI. v2's `none` model maps to it directly.
59
+
60
+ When `categories` selects only some optional categories, you must set
61
+ `scopeMode` explicitly. A strict scope blocks categories outside the rule. A permissive scope allows
62
+ those categories unless another restriction applies. An omitted scope, `['*']`,
63
+ or a list containing only `necessary` expands to the default optional categories.
64
+ `necessary` is always permitted.
65
+
66
+ `offline()` without `policyRules` resolves `recommendedPolicyRules()`, whose
67
+ Europe rule also matches a visitor with no country. A known US country with a
68
+ missing state uses US opt-out with GPC and persistent preferences; a missing
69
+ Canadian province stays strict opt-in. The last rule is `none` for other known
70
+ unmatched locations. Resolution exposes `matched`, `no-match`,
71
+ `unconfigured`, or `failed`; while nothing has matched, optional categories stay
72
+ denied and no consent surface renders. A missing or malformed policy never grants
73
+ optional permissions by itself.
74
+
75
+ ## Read the state you need
76
+
77
+ | Purpose | Snapshot field | React hook |
78
+ | ----------------------------------------------- | ---------------------- | --------------------------------------- |
79
+ | Gate scripts and optional features | `effectivePermissions` | `useConsent(category)`, `useConsents()` |
80
+ | Inspect recorded choices and confirmation times | `explicitChoice` | `useExplicitChoice()` |
81
+ | Decide whether to prompt | `promptRequirement` | `usePromptRequirement()` |
82
+ | Inspect the resolved rule | `policyRule` | `usePolicyRule()` |
83
+ | Inspect matching or failure | `resolution` | `usePolicyResolution()` |
84
+
85
+ An effective permission is not evidence of a grant. Under an opt-out rule it can
86
+ be true before a visitor acts. A notice dismissal updates `noticeDismissal` and
87
+ does not record consent. Global Privacy Control updates privacy signals and
88
+ configured opt-out directives without turning a browser signal into a choice.
89
+
90
+ ## Replace choice callbacks
91
+
92
+ ```tsx
93
+ <ConsentProvider
94
+ options={{
95
+ mode,
96
+ callbacks: {
97
+ onChoiceRecorded(event) {
98
+ console.log('Visitor recorded a choice', event);
99
+ },
100
+ onPermissionsChanged(event) {
101
+ console.log('Effective permissions changed', event);
102
+ },
103
+ },
104
+ }}
105
+ >
106
+ {children}
107
+ </ConsentProvider>
108
+ ```
109
+
110
+ Replace `onConsentSet` with `onChoiceRecorded` for explicit accept, reject, or
111
+ save actions. Use `onPermissionsChanged` for permission changes caused by a
112
+ choice, expiry, policy update, or privacy signal. Provider callbacks no longer
113
+ include `onBannerFetched`; use the resolved policy and pending state to render
114
+ loading UI.
115
+
116
+ Only `kernel.commands.save()` creates an explicit choice. `save('all')` accepts,
117
+ `save('none')` rejects, and `save({ marketing: false })` confirms only marketing.
118
+ A partial save keeps the other categories' confirmation times. Use
119
+ `commands.dismissNotice()` for the notice action and `kernel.hydrate()` to apply
120
+ validated records without recording an action.
121
+
122
+ ## Keep existing storage
123
+
124
+ The reader accepts valid v2 storage and translates it into per-category receipts.
125
+ It does not rewrite storage on startup. Existing denials continue to restrict
126
+ permissions; expired or incompatible positive receipts cannot restore grants.
127
+ The next explicit action writes the v3 record. Notice dismissal and privacy
128
+ opt-out directives have separate records.
129
+
130
+ Pass server-prepared `initialRecords`, `initialPolicyResolution`, and `now`
131
+ through the adapter's prefetch configuration. Do not rebuild consent from a
132
+ boolean `hasConsented` or copy effective permissions into explicit choice records.
133
+ Pending prefetch results cannot restore records after a clear or overwrite a
134
+ newer choice.
135
+
136
+ ## Update custom transports and backend clients
137
+
138
+ Use `KernelTransport` from `@c15t/core/transports`. Init returns a versioned
139
+ `policyResolution` with a matched rule and fingerprints, or an explicit
140
+ non-matched outcome. Use `mapInitOutputToInitResponse` for an HTTP init payload.
141
+ Do not return legacy `policy` or `policyDecision` fields.
142
+
143
+ Save requests contain the explicit choice and the categories confirmed by this
144
+ action. Return a `SaveResult` with `ok`; optional identity and record methods
145
+ must preserve the same receipt format. Forward the policy contract headers when
146
+ implementing an HTTP proxy. Keep client, schema, backend, and framework adapters
147
+ on compatible v3 versions.
148
+
149
+ Use the supplied hosted or manifest transports to capture policy evidence for
150
+ saves and retries. They preserve the action's policy, location, language, and
151
+ privacy signal inputs. A stale policy assertion is an error; it must not silently
152
+ save under a different rule. See the
153
+ [backend endpoint reference](https://c15t.com/docs/self-host/api/endpoints) for wire fields.
154
+
155
+ ## Shared runtime ownership
156
+
157
+ Astro and SvelteKit can create a runtime outside a component tree with
158
+ `createConsentRuntime` from `@c15t/core/runtime`. Pass that runtime to a
159
+ framework provider with its `runtime` prop. The owner calls `start()` after
160
+ mount and `dispose()` when the page no longer needs it. Borrowing providers
161
+ subscribe to the kernel and render UI without initializing or disposing it.
162
+
163
+ Runtime construction preserves the prepared server snapshot. Storage reads,
164
+ privacy-signal activation and script loading begin on `start()`. Pass server
165
+ records through `prefetch.initialRecords` with their evaluation time to keep
166
+ the first browser render consistent with the server. `runtime.clearRecords()`
167
+ clears both persisted and in-memory records.
168
+
169
+ Runtime callbacks use `onChoiceRecorded`, `onPermissionsChanged` and
170
+ `onError`. Hydrating records does not report a new visitor choice.
171
+
172
+ Astro's serializable offline descriptor accepts `policyRules`, just like the
173
+ other adapters' offline factories. Configure a preset or explicit rules;
174
+ omitting them keeps the conservative fallback.
175
+
176
+ ## Presentation corrections
177
+
178
+ Use `blocking` for backdrop, scroll locking and focus trapping together.
179
+ Choice banners default to non-blocking, choice walls always block, and notices
180
+ never block. A notice configured as a wall falls back to a floating card.
181
+ Preferences remain centered; `variant` and `position` apply only to prompts.
182
+
183
+ Replace reads of `uncoveredRights` with `preferenceControls`. The latter is a
184
+ list of additional preferences buttons recommended for the stock UI. It does
185
+ not verify disclosure or persistent access to policy rights.
186
+
187
+ Notice acknowledgement uses `common.acknowledge`, falling back to
188
+ `common.dismiss` for older translation bundles. It does not record a choice.
189
+
190
+ ## Astro notice acknowledgement
191
+
192
+ ```astro
193
+ ---
194
+ import ConsentBanner from '@c15t/astro/components/consent-banner.astro';
195
+ ---
196
+
197
+ <ConsentBanner dismissButtonText="Got it" />
198
+ ```
199
+
200
+ Astro's `ConsentBanner` accepts `dismissButtonText` as an optional string. It
201
+ labels the notice acknowledgement button, which defaults to
202
+ `common.acknowledge` and falls back to `common.dismiss` in older translation
203
+ bundles. Acknowledging a notice preserves the visitor's consent choices.
204
+
205
+ ## IAB blocking behavior
206
+
207
+ IAB banners and dialogs use the same `presentation.prompt.blocking` and
208
+ `presentation.preferences.blocking` settings as the other consent components.
209
+ A blocking surface shows a backdrop, traps focus and locks page scrolling.
210
+ Explicit `blocking` values take precedence over deprecated `scrollLock` and
211
+ `trapFocus` options. IAB components stay hidden without a matched policy,
212
+ including when a dialog receives `open={true}`.
213
+
214
+ The React consent dialog still blocks pointer interaction with the page when
215
+ `blocking` is true and you hide its backdrop with `overlay={false}`.
216
+
217
+ Legacy prompt options now control the whole blocking behavior. For example,
218
+ `scrollLock: true` alone enables a backdrop and focus trapping as well as
219
+ scroll locking. Set `blocking: false` explicitly for a non-modal banner.
220
+
221
+ Custom dialog backdrops follow the same rule. A dialog resolved as non-blocking
222
+ omits both the default backdrop and a supplied `overlay`. To retain a custom
223
+ backdrop when migrating from `trapFocus: false`, set
224
+ `presentation.preferences.blocking: true` and keep your `overlay` prop.
225
+
226
+ ## Next.js and TanStack Start renames
227
+
228
+ These names changed without deprecated aliases. `defineConsentConfig`,
229
+ `ConsentConfig` (the URL config) and `ConsentProvider` from `@c15t/react` are
230
+ unchanged.
231
+
232
+ Next.js (`c15t/next`, `c15t/next/server`, `c15t/next/pages`):
233
+
234
+ | v2 | v3 |
235
+ | --------------------------------------------------------------------------------------------- | ---------------------------------------------------------- |
236
+ | `ConsentBoundary` | `ConsentRoot` |
237
+ | `ConsentBoundaryProps` | `ConsentRootProps` |
238
+ | `config={initialConsent}` (the visitor's resolved state) | `state={...}` |
239
+ | `consent={consentConfig}` (the `defineConsentConfig` result) | `config={consentConfig}` |
240
+ | `prefetchInitialConsent(options)` | `resolveConsent(options)` with `config` or `backendURL` |
241
+ | `readInitialConsentConfig(options)` | `resolveConsent(options)` without `config` or `backendURL` |
242
+ | `InitialConsentConfig`, and `KernelConfig` where it named the returned value | `ConsentState` |
243
+ | `PrefetchInitialConsentOptions` | `ResolveConsentOptions` |
244
+ | `ReadInitialConsentConfigOptions` | `ConsentRequestOptions` |
245
+ | Pages Router `readInitialConsentConfig(req, opts)` and `prefetchInitialConsent({ req, ... })` | `resolveConsent({ req, ... })` |
246
+
247
+ TanStack Start (`c15t/tanstack-start`, `c15t/tanstack-start/server`):
248
+
249
+ | v2 | v3 |
250
+ | ------------------------------------- | ---------------------------------------------- |
251
+ | `ConsentBoundary` | `ConsentRoot` |
252
+ | `config={config}` | `state={state}` |
253
+ | `ConsentConfig` (the returned state) | `ConsentState` |
254
+ | `prefetchInitialConsent` | `resolveConsent(options)` with `backendURL` |
255
+ | `readInitialConsentConfig` | `resolveConsent(options)` without `backendURL` |
256
+ | `createConsentConfigHandler(options)` | `createConsentStateHandler(options)` |
257
+ | `mergeInitIntoConsentConfig` | `mergeInitIntoConsentState` |
258
+
259
+ `consentLoaderOptions`, `backendURL`, `initRoute` and `DEFAULT_INIT_ROUTE` are
260
+ unchanged.
261
+
262
+ ## `Frame` is now `ConsentGate`
263
+
264
+ The component that mounts an embed only while its category is allowed is now
265
+ `ConsentGate` in React, Next.js, TanStack Start, Svelte and Vue. The old names
266
+ still work as deprecated aliases and will be removed in a later major release.
267
+
268
+ | v2 | v3 |
269
+ | -------------------------------------------------------------------- | ---------------------------------------------------------------------------- |
270
+ | `Frame`, `Frame.Root`, `Frame.Title`, `Frame.Button` | `ConsentGate`, `ConsentGate.Root`, `ConsentGate.Title`, `ConsentGate.Button` |
271
+ | `FrameProps`, `FrameCompoundComponent` | `ConsentGateProps`, `ConsentGateCompoundComponent` |
272
+ | `c15t/react/frame`, `c15t/react/components/frame` | `c15t/react/consent-gate`, `c15t/react/components/consent-gate` |
273
+ | Vue `ConsentFrame`, `@c15t/vue/runtime/components/consent-frame.vue` | `ConsentGate`, `@c15t/vue/runtime/components/consent-gate.vue` |
274
+
275
+ Props and behavior are unchanged. The placeholder keeps its `frame.*`
276
+ translation keys, `--frame-*` CSS custom properties and
277
+ `data-testid="frame-placeholder"`, so custom copy, styles and tests continue
278
+ to apply.
279
+
280
+ ## Next.js RSC banner removal
281
+
282
+ The `@c15t/nextjs/rsc` entry (`c15t/next/rsc`) and its `RscConsentBanner`,
283
+ `RscBannerGate` and `RscBannerActions` exports are gone. Imports of that
284
+ entry now fail to resolve. Use the regular banner for server rendering.
285
+ The retained benchmark results showed slightly faster banner visibility and
286
+ interaction for the experimental shell, so this removal should not be read
287
+ as a performance improvement.
288
+
289
+ Replace `<RscConsentBanner config={config} />` with `<ConsentBanner />` inside
290
+ the same `ConsentRoot`, importing it from `c15t/next` when you use the
291
+ umbrella package or from `@c15t/nextjs` when you depend on the scoped package
292
+ directly. With an awaited `resolveConsent` result, the server renders
293
+ the banner into the response, which is what the RSC variant was for. Move
294
+ `presentation` to `options.presentation` on `ConsentRoot`.
295
+
296
+ Two defaults differ from the removed shell. It rendered no branding link, so
297
+ pass `hideBranding` to keep that. It also styled only its root and action row
298
+ (through the stock `ConsentBanner.Root` and `PolicyActions`) and left the
299
+ card, title, description, footer and buttons without base classes, whereas
300
+ `ConsentBanner` merges the stock styles into every part. If that changes a
301
+ custom layout, do not reach for `noStyle` on the whole banner, which also
302
+ drops the root positioning and action-row layout the old shell kept; compose
303
+ the compound parts instead and pass `noStyle` only to the parts you styled
304
+ yourself.
305
+
306
+ `ConsentBanner` has no `classNames` or `children` props. Move each legacy
307
+ `classNames` key to the `ConsentRoot` `options.components` slots, which take
308
+ `{ className }`:
309
+
310
+ | `classNames` key | Replacement |
311
+ | ------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
312
+ | `root`, `card`, `title`, `footer` | `components.banner.root`, `.card`, `.title`, `.footer` |
313
+ | `description` | `components.description.banner` |
314
+ | `rights`, `rightLink` | `components.banner.rights`, `components.banner.rightLink` |
315
+ | `acceptButton`, `rejectButton`, `customizeButton`, `dismissButton` | No per-button slot. Use `components.button.primary` / `.secondary` for shared styles, or target `[data-action="accept"]`, `[data-action="reject"]`, `[data-action="customize"]` and `[data-action="dismiss"]` in CSS. |
316
+
317
+ Custom `children`, and any direct use of `RscBannerGate` or
318
+ `RscBannerActions`, map to the compound parts of `ConsentBanner` in a Client
319
+ Component. `RscBannerGate` becomes `ConsentBanner.Root`, which mounts only
320
+ while the policy owes a prompt and reopens on expiry. Remove the gate
321
+ `prompt` and `model` props; the root derives `data-prompt` and `data-model`
322
+ from the `ConsentRoot` policy. Rename `title` to `aria-label` to preserve its
323
+ accessible label. Keep `children`, `className`, `variant`, `position` and
324
+ `blocking` on the root. Add `disableAnimation` and `trapFocus={false}` to
325
+ retain the old gate defaults.
326
+
327
+ `RscBannerActions`
328
+ becomes `ConsentBanner.PolicyActions`, which renders the rights links and the
329
+ action row the policy requires; `ConsentBanner.Rights`, `RightLink`, `Footer`,
330
+ `FooterSubGroup` and the four buttons are available for finer control.
331
+ `PolicyActions` takes none of the old `acceptLabel`, `rejectLabel`,
332
+ `customizeLabel`, `dismissLabel`, `rightLabels` or `classNames` props: set
333
+ labels through the provider `i18n` translation overrides (`common.acceptAll`,
334
+ `common.rejectAll`, `common.customize`, `common.acknowledge`, `rights.*`), or
335
+ render `ConsentBanner.AcceptButton` and the other buttons with your own
336
+ children; `renderAction` on `PolicyActions` replaces one button; classes move
337
+ to the `components` slots listed in the table. Place the former `children`
338
+ inside `ConsentBanner.Card`:
339
+
340
+ ```tsx title="components/banner.tsx"
341
+ 'use client';
342
+
343
+ import { ConsentBanner } from 'c15t/next';
344
+
345
+ export function Banner() {
346
+ return (
347
+ <ConsentBanner.Root>
348
+ <ConsentBanner.Card>
349
+ <ConsentBanner.Header>
350
+ <ConsentBanner.Title />
351
+ <ConsentBanner.Description />
352
+ </ConsentBanner.Header>
353
+ <a href="/privacy">Privacy policy</a>
354
+ <ConsentBanner.PolicyActions />
355
+ </ConsentBanner.Card>
356
+ </ConsentBanner.Root>
357
+ );
358
+ }
359
+ ```
360
+
361
+ ## Session reports in manifest mode
362
+
363
+ A host that resolves init from a cached manifest never calls `/init`, so a v3
364
+ backend could not count the visitors it served. From this release every
365
+ server-side resolution sends `POST /sessions` to the backend after the fact,
366
+ server-to-server and detached from the response: the Next.js, TanStack Start,
367
+ SvelteKit, Nuxt and Astro init routes, and the Next.js, TanStack Start and
368
+ Astro render-time prefetches. The browser makes no request.
369
+
370
+ This is a new outbound data flow from your server to the consent backend. The
371
+ report carries the manifest revision, the matched policy, the jurisdiction,
372
+ country, region, language and GPC signal, plus the visitor's user agent and
373
+ address as request headers. It carries no cookies and no identifier. Nothing is
374
+ stored on the backend; the report reaches a `sessions.onReport` sink and the
375
+ request log.
376
+
377
+ Reporting is on by default and needs an absolute backend URL. Set
378
+ `reportSessions: false` on the adapter to send none. Each report is one
379
+ resolution, not one visitor: a page view can produce a `render` report and a
380
+ `route` report, and nothing in them links the two. A consumer counting sessions
381
+ groups reports by the forwarded address and user agent within a window.