@c15t/scripts 3.0.0-alpha.2 → 3.0.0-alpha.4

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 (300) hide show
  1. package/AGENTS.md +129 -63
  2. package/README.md +8 -29
  3. package/SKILL.md +33 -0
  4. package/dist/adobe-analytics.js +2 -0
  5. package/dist/ahrefs-analytics.js +2 -0
  6. package/dist/amplitude.js +2 -0
  7. package/dist/clearbit.js +2 -0
  8. package/dist/cloudflare-web-analytics.js +2 -0
  9. package/dist/cloudflare-zaraz.js +2 -0
  10. package/dist/crisp.js +2 -0
  11. package/dist/databuddy.js +2 -0
  12. package/dist/e2e-test-utils.js +2 -139
  13. package/dist/engine/compile.js +2 -89
  14. package/dist/engine/runtime.js +2 -448
  15. package/dist/events.js +2 -218
  16. package/dist/fathom-analytics.js +2 -0
  17. package/dist/front-chat.js +2 -0
  18. package/dist/google-tag-manager.js +2 -0
  19. package/dist/google-tag.js +2 -0
  20. package/dist/heap.js +2 -0
  21. package/dist/hightouch.js +2 -0
  22. package/dist/hotjar.js +2 -0
  23. package/dist/intercom.js +2 -0
  24. package/dist/klaviyo.js +2 -0
  25. package/dist/linkedin-insights.js +2 -0
  26. package/dist/logrocket.js +2 -0
  27. package/dist/matomo-analytics.js +2 -0
  28. package/dist/meta-pixel.js +2 -0
  29. package/dist/microsoft-clarity.js +2 -0
  30. package/dist/microsoft-uet.js +2 -0
  31. package/dist/mixpanel-analytics.js +2 -0
  32. package/dist/one-dollar-stats.js +2 -0
  33. package/dist/openai-pixel.js +2 -0
  34. package/dist/pinterest-tag.js +2 -0
  35. package/dist/pirsch.js +2 -0
  36. package/dist/plausible-analytics.js +2 -0
  37. package/dist/posthog.js +2 -0
  38. package/dist/promptwatch.js +2 -0
  39. package/dist/reddit-pixel.js +2 -0
  40. package/dist/registry.js +2 -422
  41. package/dist/resolve.js +2 -33
  42. package/dist/rudderstack.js +2 -0
  43. package/dist/rybbit-analytics.js +2 -0
  44. package/dist/segment.js +2 -0
  45. package/dist/snapchat-pixel.js +2 -0
  46. package/dist/tiktok-pixel.js +2 -0
  47. package/dist/types.js +2 -16
  48. package/dist/umami-analytics.js +2 -0
  49. package/dist/vendors/_shared/attributes.js +2 -14
  50. package/dist/vendors/_shared/google-consent.js +2 -27
  51. package/dist/vendors/_shared/install-builders.js +2 -21
  52. package/dist/vendors/_shared/required-id.js +2 -0
  53. package/dist/vendors/_shared/script-url.js +2 -28
  54. package/dist/vendors/ads-and-pixels/linkedin-insights.js +2 -48
  55. package/dist/vendors/ads-and-pixels/meta-pixel.js +2 -153
  56. package/dist/vendors/ads-and-pixels/microsoft-uet.js +2 -110
  57. package/dist/vendors/ads-and-pixels/openai-pixel.js +2 -88
  58. package/dist/vendors/ads-and-pixels/pinterest-tag.js +2 -123
  59. package/dist/vendors/ads-and-pixels/reddit-pixel.js +2 -107
  60. package/dist/vendors/ads-and-pixels/snapchat-pixel.js +2 -87
  61. package/dist/vendors/ads-and-pixels/tiktok-pixel.js +2 -89
  62. package/dist/vendors/ads-and-pixels/x-pixel.js +2 -48
  63. package/dist/vendors/analytics/adobe-analytics.js +2 -49
  64. package/dist/vendors/analytics/ahrefs-analytics.js +2 -27
  65. package/dist/vendors/analytics/amplitude.js +2 -134
  66. package/dist/vendors/analytics/clearbit.js +2 -28
  67. package/dist/vendors/analytics/cloudflare-web-analytics.js +2 -32
  68. package/dist/vendors/analytics/databuddy.js +2 -103
  69. package/dist/vendors/analytics/fathom-analytics.js +2 -35
  70. package/dist/vendors/analytics/google-tag.js +2 -78
  71. package/dist/vendors/analytics/heap.js +2 -134
  72. package/dist/vendors/analytics/hightouch.js +2 -109
  73. package/dist/vendors/analytics/hotjar.js +2 -44
  74. package/dist/vendors/analytics/logrocket.js +2 -58
  75. package/dist/vendors/analytics/matomo-analytics.js +2 -191
  76. package/dist/vendors/analytics/microsoft-clarity.js +2 -100
  77. package/dist/vendors/analytics/mixpanel-analytics.js +2 -93
  78. package/dist/vendors/analytics/one-dollar-stats.js +2 -30
  79. package/dist/vendors/analytics/pirsch.js +2 -67
  80. package/dist/vendors/analytics/plausible-analytics.js +2 -81
  81. package/dist/vendors/analytics/posthog.js +2 -200
  82. package/dist/vendors/analytics/promptwatch.js +2 -29
  83. package/dist/vendors/analytics/rudderstack.js +2 -183
  84. package/dist/vendors/analytics/rybbit-analytics.js +2 -63
  85. package/dist/vendors/analytics/segment.js +2 -65
  86. package/dist/vendors/analytics/umami-analytics.js +2 -39
  87. package/dist/vendors/analytics/vercel-analytics.js +2 -53
  88. package/dist/vendors/email-and-sms/klaviyo.js +2 -0
  89. package/dist/vendors/functional/crisp.js +2 -100
  90. package/dist/vendors/functional/front-chat.js +2 -64
  91. package/dist/vendors/functional/intercom.js +2 -45
  92. package/dist/vendors/tag-managers/cloudflare-zaraz.js +2 -98
  93. package/dist/vendors/tag-managers/google-tag-manager.js +2 -73
  94. package/dist/vercel-analytics.js +2 -0
  95. package/dist/x-pixel.js +2 -0
  96. package/dist-types/adobe-analytics.d.ts +2 -0
  97. package/dist-types/ahrefs-analytics.d.ts +2 -0
  98. package/dist-types/amplitude.d.ts +2 -0
  99. package/dist-types/clearbit.d.ts +2 -0
  100. package/dist-types/cloudflare-web-analytics.d.ts +2 -0
  101. package/dist-types/cloudflare-zaraz.d.ts +2 -0
  102. package/dist-types/crisp.d.ts +2 -0
  103. package/dist-types/databuddy.d.ts +2 -0
  104. package/dist-types/e2e-test-utils.d.ts +2 -0
  105. package/dist-types/engine/compile.d.ts +2 -3
  106. package/dist-types/engine/runtime.d.ts +2 -3
  107. package/dist-types/events.d.ts +2 -46
  108. package/dist-types/fathom-analytics.d.ts +2 -0
  109. package/dist-types/front-chat.d.ts +2 -0
  110. package/dist-types/google-tag-manager.d.ts +2 -0
  111. package/dist-types/google-tag.d.ts +2 -0
  112. package/dist-types/heap.d.ts +2 -0
  113. package/dist-types/hightouch.d.ts +2 -0
  114. package/dist-types/hotjar.d.ts +2 -0
  115. package/dist-types/intercom.d.ts +2 -0
  116. package/dist-types/klaviyo.d.ts +2 -0
  117. package/dist-types/linkedin-insights.d.ts +2 -0
  118. package/dist-types/logrocket.d.ts +2 -0
  119. package/dist-types/matomo-analytics.d.ts +2 -0
  120. package/dist-types/meta-pixel.d.ts +2 -0
  121. package/dist-types/microsoft-clarity.d.ts +2 -0
  122. package/dist-types/microsoft-uet.d.ts +2 -0
  123. package/dist-types/mixpanel-analytics.d.ts +2 -0
  124. package/dist-types/one-dollar-stats.d.ts +2 -0
  125. package/dist-types/openai-pixel.d.ts +2 -0
  126. package/dist-types/pinterest-tag.d.ts +2 -0
  127. package/dist-types/pirsch.d.ts +2 -0
  128. package/dist-types/plausible-analytics.d.ts +2 -0
  129. package/dist-types/posthog.d.ts +2 -0
  130. package/dist-types/promptwatch.d.ts +2 -0
  131. package/dist-types/reddit-pixel.d.ts +2 -0
  132. package/dist-types/registry.d.ts +2 -485
  133. package/dist-types/resolve.d.ts +2 -9
  134. package/dist-types/rudderstack.d.ts +2 -0
  135. package/dist-types/rybbit-analytics.d.ts +2 -0
  136. package/dist-types/segment.d.ts +2 -0
  137. package/dist-types/snapchat-pixel.d.ts +2 -0
  138. package/dist-types/tiktok-pixel.d.ts +2 -0
  139. package/dist-types/types.d.ts +2 -314
  140. package/dist-types/umami-analytics.d.ts +2 -0
  141. package/dist-types/vendors/_shared/attributes.d.ts +2 -35
  142. package/dist-types/vendors/_shared/google-consent.d.ts +2 -47
  143. package/dist-types/vendors/_shared/install-builders.d.ts +2 -30
  144. package/dist-types/vendors/_shared/required-id.d.ts +2 -0
  145. package/dist-types/vendors/_shared/script-url.d.ts +2 -75
  146. package/dist-types/vendors/ads-and-pixels/linkedin-insights.d.ts +2 -92
  147. package/dist-types/vendors/ads-and-pixels/meta-pixel.d.ts +2 -289
  148. package/dist-types/vendors/ads-and-pixels/microsoft-uet.d.ts +2 -105
  149. package/dist-types/vendors/ads-and-pixels/openai-pixel.d.ts +2 -211
  150. package/dist-types/vendors/ads-and-pixels/pinterest-tag.d.ts +2 -295
  151. package/dist-types/vendors/ads-and-pixels/reddit-pixel.d.ts +2 -210
  152. package/dist-types/vendors/ads-and-pixels/snapchat-pixel.d.ts +2 -171
  153. package/dist-types/vendors/ads-and-pixels/tiktok-pixel.d.ts +2 -106
  154. package/dist-types/vendors/ads-and-pixels/x-pixel.d.ts +2 -183
  155. package/dist-types/vendors/analytics/adobe-analytics.d.ts +2 -75
  156. package/dist-types/vendors/analytics/ahrefs-analytics.d.ts +2 -62
  157. package/dist-types/vendors/analytics/amplitude.d.ts +2 -234
  158. package/dist-types/vendors/analytics/clearbit.d.ts +2 -60
  159. package/dist-types/vendors/analytics/cloudflare-web-analytics.d.ts +2 -67
  160. package/dist-types/vendors/analytics/databuddy.d.ts +2 -147
  161. package/dist-types/vendors/analytics/fathom-analytics.d.ts +2 -90
  162. package/dist-types/vendors/analytics/google-tag.d.ts +2 -95
  163. package/dist-types/vendors/analytics/heap.d.ts +2 -316
  164. package/dist-types/vendors/analytics/hightouch.d.ts +2 -285
  165. package/dist-types/vendors/analytics/hotjar.d.ts +2 -73
  166. package/dist-types/vendors/analytics/logrocket.d.ts +2 -101
  167. package/dist-types/vendors/analytics/matomo-analytics.d.ts +2 -41
  168. package/dist-types/vendors/analytics/microsoft-clarity.d.ts +2 -97
  169. package/dist-types/vendors/analytics/mixpanel-analytics.d.ts +2 -113
  170. package/dist-types/vendors/analytics/one-dollar-stats.d.ts +2 -39
  171. package/dist-types/vendors/analytics/pirsch.d.ts +2 -96
  172. package/dist-types/vendors/analytics/plausible-analytics.d.ts +2 -122
  173. package/dist-types/vendors/analytics/posthog.d.ts +2 -175
  174. package/dist-types/vendors/analytics/promptwatch.d.ts +2 -36
  175. package/dist-types/vendors/analytics/rudderstack.d.ts +2 -330
  176. package/dist-types/vendors/analytics/rybbit-analytics.d.ts +2 -82
  177. package/dist-types/vendors/analytics/segment.d.ts +2 -164
  178. package/dist-types/vendors/analytics/umami-analytics.d.ts +2 -93
  179. package/dist-types/vendors/analytics/vercel-analytics.d.ts +2 -66
  180. package/dist-types/vendors/email-and-sms/klaviyo.d.ts +2 -0
  181. package/dist-types/vendors/functional/crisp.d.ts +2 -78
  182. package/dist-types/vendors/functional/front-chat.d.ts +2 -62
  183. package/dist-types/vendors/functional/intercom.d.ts +2 -135
  184. package/dist-types/vendors/tag-managers/cloudflare-zaraz.d.ts +2 -39
  185. package/dist-types/vendors/tag-managers/google-tag-manager.d.ts +2 -96
  186. package/dist-types/vercel-analytics.d.ts +2 -0
  187. package/dist-types/x-pixel.d.ts +2 -0
  188. package/docs/README.md +129 -63
  189. package/docs/assets/v3/bottom-bar.png +0 -0
  190. package/docs/assets/v3/brand-card.png +0 -0
  191. package/docs/assets/v3/brand-preferences.png +0 -0
  192. package/docs/assets/v3/choice-wall.png +0 -0
  193. package/docs/assets/v3/headless-bar-html.png +0 -0
  194. package/docs/assets/v3/headless-bar-mobile.png +0 -0
  195. package/docs/assets/v3/headless-bar.png +0 -0
  196. package/docs/assets/v3/slim-bar.png +0 -0
  197. package/docs/concepts/choose-your-setup.md +87 -0
  198. package/docs/concepts/consent-categories.md +84 -0
  199. package/docs/{guides → concepts}/consent-state.md +89 -105
  200. package/docs/{guides → concepts}/data-fetching.md +31 -27
  201. package/docs/concepts/how-consent-works.md +123 -0
  202. package/docs/concepts/policies.md +71 -0
  203. package/docs/customization/class-names.md +202 -0
  204. package/docs/customization/dark-mode.md +157 -0
  205. package/docs/customization/motion.md +119 -0
  206. package/docs/customization/overview.md +67 -34
  207. package/docs/customization/recipes.md +839 -49
  208. package/docs/customization/slots.md +216 -35
  209. package/docs/customization/stylesheets.md +147 -0
  210. package/docs/customization/tailwind.md +842 -0
  211. package/docs/customization/tokens.md +163 -96
  212. package/docs/customization/translations.md +60 -3
  213. package/docs/frameworks/astro/embeds.md +160 -0
  214. package/docs/frameworks/astro/network-blocker.md +86 -0
  215. package/docs/frameworks/astro/scripts.md +155 -0
  216. package/docs/frameworks/html/embeds.md +142 -0
  217. package/docs/frameworks/html/network-blocker.md +105 -0
  218. package/docs/frameworks/html/scripts.md +164 -0
  219. package/docs/frameworks/javascript/scripts.md +137 -0
  220. package/docs/frameworks/next/embeds.md +90 -0
  221. package/docs/frameworks/next/network-blocker.md +153 -0
  222. package/docs/frameworks/next/scripts.md +196 -0
  223. package/docs/frameworks/nuxt/embeds.md +81 -0
  224. package/docs/frameworks/nuxt/network-blocker.md +97 -0
  225. package/docs/frameworks/nuxt/scripts.md +89 -0
  226. package/docs/frameworks/react/embeds.md +89 -0
  227. package/docs/frameworks/react/network-blocker.md +140 -0
  228. package/docs/frameworks/react/scripts.md +115 -0
  229. package/docs/frameworks/svelte/embeds.md +96 -0
  230. package/docs/frameworks/svelte/network-blocker.md +141 -0
  231. package/docs/frameworks/svelte/scripts.md +144 -0
  232. package/docs/frameworks/sveltekit/embeds.md +103 -0
  233. package/docs/frameworks/sveltekit/network-blocker.md +159 -0
  234. package/docs/frameworks/sveltekit/scripts.md +172 -0
  235. package/docs/frameworks/tanstack-start/embeds.md +96 -0
  236. package/docs/frameworks/tanstack-start/network-blocker.md +145 -0
  237. package/docs/frameworks/tanstack-start/scripts.md +103 -0
  238. package/docs/frameworks/vue/embeds.md +84 -0
  239. package/docs/frameworks/vue/network-blocker.md +99 -0
  240. package/docs/frameworks/vue/scripts.md +93 -0
  241. package/docs/guides/banner-experiments.md +654 -0
  242. package/docs/guides/troubleshooting.md +120 -47
  243. package/docs/guides/verify-consent.md +81 -49
  244. package/docs/integrations/adobe-analytics.md +167 -159
  245. package/docs/integrations/ahrefs-analytics.md +152 -154
  246. package/docs/integrations/amplitude.md +162 -156
  247. package/docs/integrations/building-integrations.md +136 -37
  248. package/docs/integrations/clearbit.md +154 -154
  249. package/docs/integrations/cloudflare-web-analytics.md +156 -156
  250. package/docs/integrations/cloudflare-zaraz.md +209 -261
  251. package/docs/integrations/crisp.md +164 -158
  252. package/docs/integrations/databuddy.md +157 -173
  253. package/docs/integrations/fathom-analytics.md +158 -156
  254. package/docs/integrations/front-chat.md +167 -167
  255. package/docs/integrations/google-maps.md +118 -83
  256. package/docs/integrations/google-tag-manager.md +178 -163
  257. package/docs/integrations/google-tag.md +163 -160
  258. package/docs/integrations/heap.md +163 -155
  259. package/docs/integrations/hightouch.md +161 -157
  260. package/docs/integrations/hotjar.md +159 -155
  261. package/docs/integrations/intercom.md +183 -153
  262. package/docs/integrations/klaviyo.md +486 -0
  263. package/docs/integrations/linkedin-insights.md +174 -150
  264. package/docs/integrations/logrocket.md +160 -156
  265. package/docs/integrations/matomo-analytics.md +188 -178
  266. package/docs/integrations/meta-pixel.md +188 -150
  267. package/docs/integrations/microsoft-clarity.md +163 -155
  268. package/docs/integrations/microsoft-uet.md +148 -154
  269. package/docs/integrations/mixpanel-analytics.md +155 -160
  270. package/docs/integrations/one-dollar-stats.md +166 -165
  271. package/docs/integrations/openai-pixel.md +204 -301
  272. package/docs/integrations/overview.md +137 -80
  273. package/docs/integrations/pinterest-tag.md +191 -183
  274. package/docs/integrations/pirsch.md +169 -159
  275. package/docs/integrations/plausible-analytics.md +172 -158
  276. package/docs/integrations/posthog.md +313 -240
  277. package/docs/integrations/promptwatch.md +154 -154
  278. package/docs/integrations/reddit-pixel.md +185 -157
  279. package/docs/integrations/rudderstack.md +201 -186
  280. package/docs/integrations/rybbit-analytics.md +171 -160
  281. package/docs/integrations/segment.md +182 -154
  282. package/docs/integrations/snapchat-pixel.md +186 -156
  283. package/docs/integrations/tiktok-pixel.md +171 -150
  284. package/docs/integrations/umami-analytics.md +163 -158
  285. package/docs/integrations/vercel-analytics.md +166 -157
  286. package/docs/integrations/x-pixel.md +176 -150
  287. package/docs/integrations/youtube.md +121 -86
  288. package/docs/upgrade-v3.md +496 -467
  289. package/package.json +10 -257
  290. package/dist-types/__tests__/helpers.d.ts +0 -141
  291. package/docs/assets/v3/brand-bar.png +0 -0
  292. package/docs/assets/v3/mobile-card.png +0 -0
  293. package/docs/assets/v3/preferences.png +0 -0
  294. package/docs/frameworks/javascript/script-loader.md +0 -100
  295. package/docs/frameworks/next/script-loader.md +0 -216
  296. package/docs/frameworks/react/script-loader.md +0 -69
  297. package/docs/guides/deployment-modes.md +0 -75
  298. package/docs/guides/shared-consent-controls.md +0 -158
  299. package/docs/integrations/clear-on-revocation.md +0 -167
  300. package/docs/integrations/granular-consent.md +0 -210
@@ -0,0 +1,97 @@
1
+ ---
2
+ title: Network blocker
3
+ description: Hold fetch and XMLHttpRequest calls in a Nuxt app until their
4
+ consent category is allowed, with rules in the c15t module options.
5
+ group: frameworks
6
+ ---
7
+
8
+ ## Add rules
9
+
10
+ The network blocker stops `fetch` and `XMLHttpRequest` calls in the browser
11
+ that match a rule until the rule's category is allowed. Use it as a backstop
12
+ for tracking calls your own code or a vendor SDK makes. Load vendor SDKs
13
+ through [`scripts`](./scripts.md) first, so they do not run
14
+ at all before consent.
15
+
16
+ Rules are plain data, so they can go in the module options:
17
+
18
+ ```ts title="nuxt.config.ts"
19
+ export default defineNuxtConfig({
20
+ c15t: {
21
+ networkBlocker: {
22
+ rules: [{ category: 'measurement', domain: 'google-analytics.com' }],
23
+ },
24
+ },
25
+ });
26
+ ```
27
+
28
+ Module options reach the browser as JSON, so the `onRequestBlocked` callback
29
+ goes under the `c15t` key of `app/app.config.ts`. Rules you set there are
30
+ added to the rules from `nuxt.config.ts`:
31
+
32
+ ```ts title="app/app.config.ts"
33
+ export default defineAppConfig({
34
+ c15t: {
35
+ networkBlocker: {
36
+ onRequestBlocked: (info) => console.info('Blocked', info.url),
37
+ rules: [],
38
+ },
39
+ },
40
+ });
41
+ ```
42
+
43
+ ## Match requests with rules
44
+
45
+ Each rule names a `domain` and the consent `category` a request needs. The
46
+ domain also matches its subdomains, so `google-analytics.com` covers
47
+ `www.google-analytics.com`. `pathIncludes` narrows a rule to paths that
48
+ contain a substring, and `methods` narrows it to HTTP methods. `category`
49
+ also takes conditions such as `{ and: ['measurement', 'marketing'] }`.
50
+
51
+ Add `vendor` with a vendor ID to also block the request while the visitor has
52
+ turned that vendor off. Rules for IAB TCF vendors use `vendorId` and the
53
+ `iabPurposes` fields instead.
54
+
55
+ | Option | Default | Purpose |
56
+ | -------------------- | -------- | ----------------------------------------------------------------------------------- |
57
+ | `rules` | Required | The rules above. |
58
+ | `enabled` | `true` | `false` keeps the rules but stops blocking. |
59
+ | `logBlockedRequests` | `true` | Logs each blocked request with `console.warn`. |
60
+ | `onRequestBlocked` | Unset | `app.config.ts` only. Called with `{ method, url, rule }` for each blocked request. |
61
+
62
+ ## What a blocked request looks like
63
+
64
+ A blocked `fetch` resolves to a `451` response with the status text
65
+ `Request blocked by consent`, and nothing is sent. A blocked XHR is aborted
66
+ and fires an `error` event. Requests that match no rule are not delayed.
67
+
68
+ ## When blocking starts
69
+
70
+ The blocker runs in the browser only. It starts holding matching requests
71
+ when the c15t plugin runs, before your app hydrates. While consent is
72
+ unknown, a matching request waits instead of failing. Once the policy and the
73
+ stored choice apply, a waiting request is sent if consent allows it and
74
+ blocked otherwise. With `manifest: 'server'`, the policy arrives with the
75
+ page, so waiting requests are decided as soon as the app mounts.
76
+
77
+ Requests your server makes during server rendering, such as `useFetch` on the
78
+ server, are not blocked. Check `useConsent()` before you call a vendor from
79
+ server code.
80
+
81
+ ## What it cannot stop
82
+
83
+ The blocker sees only browser `fetch` and `XMLHttpRequest` calls made after
84
+ the c15t plugin runs. It cannot stop:
85
+
86
+ * Scripts you add to the page head with `useHead` or `app.head`, and Nuxt
87
+ plugins that run before the c15t plugin.
88
+ * Code that saved its own reference to `fetch` before the plugin ran.
89
+ * `navigator.sendBeacon`, `WebSocket`, `EventSource`, and requests from
90
+ `<img>`, `<script>` and `<iframe>` elements, web workers and service
91
+ workers.
92
+
93
+ ## Verify
94
+
95
+ Open the Network tab, clear site data and reload. Requests that match a rule
96
+ do not appear until you allow their category, and the console lists each
97
+ blocked request. Reject, reload, and check that they still do not appear.
@@ -0,0 +1,89 @@
1
+ ---
2
+ title: Scripts
3
+ description: Load vendor scripts by consent category in a Nuxt app with the c15t
4
+ Nuxt module, and what happens when a visitor withdraws consent.
5
+ group: frameworks
6
+ ---
7
+
8
+ ## Register vendor scripts
9
+
10
+ A banner does not stop a script you load with a `<script>` tag, `useHead` or a
11
+ vendor's Nuxt module. Remove those loaders and register the vendor with c15t
12
+ instead, so c15t loads it only while its category is allowed.
13
+
14
+ Create the script configuration with the helpers from `@c15t/integrations`:
15
+
16
+ ```ts title="app/consent-scripts.ts"
17
+ import { posthog } from '@c15t/integrations/posthog';
18
+ import { xPixel } from '@c15t/integrations/x-pixel';
19
+
20
+ export const scripts = [
21
+ posthog({
22
+ id: 'phc_your_project_key',
23
+ initOptions: { cookieless_mode: 'never' },
24
+ loadMode: 'after-consent',
25
+ region: 'eu',
26
+ }),
27
+ xPixel({ pixelId: 'your-pixel-id' }),
28
+ ];
29
+ ```
30
+
31
+ Register it under the `c15t` key in `app/app.config.ts`:
32
+
33
+ ```ts title="app/app.config.ts"
34
+ import { scripts } from './consent-scripts';
35
+
36
+ export default defineAppConfig({ c15t: { scripts } });
37
+ ```
38
+
39
+ The module starts one script loader in the browser after hydration, once it
40
+ has applied the visitor's stored choice and privacy signals. Do not also call
41
+ `createScriptLoader` yourself, or each script loads twice.
42
+
43
+ `scripts` must go in `app.config.ts`. Module options in `nuxt.config.ts`
44
+ reach the browser as JSON, which drops the functions inside each script.
45
+
46
+ Every vendor guide under [integrations](../../integrations/overview.md) gives the
47
+ helper and options for that vendor.
48
+
49
+ ## Embeds and other requests
50
+
51
+ Scripts cover vendor code c15t loads for you. For the rest:
52
+
53
+ * [Embeds](./embeds.md) gates iframes with `ConsentGate` or
54
+ the iframe blocker.
55
+ * [Network blocker](./network-blocker.md) holds `fetch` and
56
+ XHR calls that match a rule until their category is allowed.
57
+
58
+ ## When a visitor withdraws consent
59
+
60
+ Removing a script tag cannot stop code that already ran. When a save turns off
61
+ a category or vendor that was allowed, c15t reloads the page so the new
62
+ document starts with only permitted code. Set `reloadOnConsentRevoked: false`
63
+ to handle revocation yourself, or use the
64
+ [`onBeforeConsentRevocationReload` callback](https://c15t.com/docs/frameworks/nuxt/callbacks#before-a-revocation-reload)
65
+ to run code before the reload.
66
+
67
+ `clearOnRevocation` deletes first-party cookies and storage keys that belong to
68
+ a category when it is withdrawn. It is plain data, so it can go in
69
+ `nuxt.config.ts`. See [clear on revocation](https://c15t.com/docs/frameworks/nuxt/clear-on-revocation).
70
+
71
+ ## Let visitors turn off one vendor
72
+
73
+ A visitor can allow marketing and still switch off one vendor in it. Declare
74
+ the vendors in the `vendors` option; helpers from `@c15t/integrations` already carry
75
+ their vendor slug. See [vendor consent](https://c15t.com/docs/frameworks/nuxt/vendor-consent).
76
+
77
+ ## Content Security Policy
78
+
79
+ Allow each vendor's script host in your `script-src` directive. The module's
80
+ `nonce` option is fixed when the app builds, so prefer the host allowlist.
81
+ See [Content Security Policy](https://c15t.com/docs/frameworks/nuxt/content-security-policy).
82
+
83
+ ## Verify gating
84
+
85
+ In a private window, open the Network tab and load a page under a policy that
86
+ asks for consent. Requests to your vendors are absent.
87
+ Allow one category and save, and only that category's vendors load. Withdraw
88
+ it, and the page reloads without loading the vendor again.
89
+ [Verify consent](../../guides/verify-consent.md) has the full checklist.
@@ -0,0 +1,89 @@
1
+ ---
2
+ title: Embeds
3
+ description: Keep YouTube videos, maps and other iframes out of a React page
4
+ until their consent category is allowed, with ConsentGate or the iframe
5
+ blocker in ConsentProvider.
6
+ group: frameworks
7
+ ---
8
+
9
+ ## Pick a method
10
+
11
+ | Method | Use it when |
12
+ | ------------------ | -------------------------------------------------------------------------------------------------------- |
13
+ | `ConsentGate` | You render the iframe in a React component and want a placeholder until the visitor allows its category. |
14
+ | The iframe blocker | The iframe comes from markup you do not render with React, such as CMS or Markdown content. |
15
+
16
+ Both keep the iframe's `src` out of the page until the category is allowed, so
17
+ the embed's host receives no request before consent.
18
+
19
+ ## Gate an embed with ConsentGate
20
+
21
+ Wrap the iframe in `ConsentGate` anywhere inside `ConsentProvider`:
22
+
23
+ ```tsx title="src/video-embed.tsx"
24
+ import { ConsentDialogLink, ConsentGate } from 'c15t/react';
25
+
26
+ export const VideoEmbed = () => (
27
+ <ConsentGate
28
+ category="measurement"
29
+ placeholder={
30
+ <div className="placeholder">
31
+ <p>Allow measurement to load this YouTube video.</p>
32
+ <ConsentDialogLink>Choose video permissions</ConsentDialogLink>
33
+ </div>
34
+ }
35
+ >
36
+ <iframe
37
+ title="YouTube video"
38
+ sandbox="allow-scripts allow-same-origin allow-presentation"
39
+ src="https://www.youtube-nocookie.com/embed/czTksCF6X8Y?playsinline=1"
40
+ allow="encrypted-media; picture-in-picture"
41
+ allowFullScreen
42
+ />
43
+ </ConsentGate>
44
+ );
45
+ ```
46
+
47
+ The iframe is absent from the page until the visitor allows measurement. The
48
+ `placeholder` shows a message and a
49
+ [`ConsentDialogLink`](https://c15t.com/docs/frameworks/react/components/consent-dialog-link)
50
+ until then. When the visitor withdraws measurement, React removes the iframe.
51
+
52
+ Pick the category that matches what the embed does. The example uses
53
+ measurement for YouTube because its player measures views. A map or chat
54
+ widget usually belongs under functionality or experience.
55
+ [ConsentGate](https://c15t.com/docs/frameworks/react/components/consent-gate) documents the
56
+ built-in placeholder and every prop.
57
+
58
+ ## Gate an iframe with the iframe blocker
59
+
60
+ `ConsentProvider` runs the iframe blocker by default. Give an iframe
61
+ `data-src` instead of `src`, and a `data-category`:
62
+
63
+ ```html title="Markup from your CMS"
64
+ <iframe
65
+ data-src="https://www.google.com/maps/embed?pb=YOUR_EMBED_ID"
66
+ data-category="functionality"
67
+ title="Store map"
68
+ ></iframe>
69
+ ```
70
+
71
+ c15t sets `src` from `data-src` when the category is allowed and removes `src`
72
+ when the category is withdrawn. It watches the page, so iframes added later
73
+ are handled too. Add `data-vendor` with a vendor ID to also block the iframe
74
+ while the visitor has turned that vendor off.
75
+
76
+ To turn the blocker off, set `iframeBlocker: false` in the provider `options`.
77
+
78
+ ## Verify the embeds
79
+
80
+ Open the production build in a private window with DevTools open, under a
81
+ policy that asks for consent.
82
+
83
+ 1. No iframe has a `src` pointing at `youtube-nocookie.com` or
84
+ `google.com/maps`, and the Network panel shows no request to them.
85
+ 2. Allow the embed's category and save. The iframe loads.
86
+ 3. Withdraw the category and save. The page reloads without the embed.
87
+
88
+ The [YouTube](../../integrations/youtube.md) and
89
+ [Google Maps](../../integrations/google-maps.md) guides cover sizing and titles.
@@ -0,0 +1,140 @@
1
+ ---
2
+ title: Network blocker
3
+ description: Hold fetch and XMLHttpRequest calls in a React app until their
4
+ consent category is allowed, with networkBlocker rules in the ConsentProvider
5
+ options.
6
+ group: frameworks
7
+ ---
8
+
9
+ ## Add rules
10
+
11
+ Some SDKs are already on the page and send beacons or API calls of their own.
12
+ Add `networkBlocker` rules to the provider options to stop `fetch` and
13
+ `XMLHttpRequest` calls to those domains until the category is allowed. Load
14
+ vendor SDKs through [`scripts`](./scripts.md) first, so they
15
+ do not run at all before consent.
16
+
17
+ This partial example adds the option next to the existing `mode` and
18
+ `scripts`:
19
+
20
+ ```tsx title="src/consent.tsx"
21
+ const networkBlocker = {
22
+ rules: [
23
+ {
24
+ id: 'google-analytics',
25
+ domain: 'google-analytics.com',
26
+ category: 'measurement',
27
+ },
28
+ {
29
+ id: 'meta-pixel',
30
+ domain: 'facebook.com',
31
+ pathIncludes: '/tr',
32
+ category: 'marketing',
33
+ },
34
+ ],
35
+ };
36
+
37
+ <ConsentProvider options={{ mode, scripts, networkBlocker }}>
38
+ ```
39
+
40
+ ## Match requests with rules
41
+
42
+ Each rule names a `domain` and the consent `category` a request needs. The
43
+ domain also matches its subdomains: `google-analytics.com` covers
44
+ `www.google-analytics.com`. `pathIncludes` narrows the rule to paths that
45
+ contain a substring, and `methods` narrows it to HTTP methods. A request is
46
+ blocked when a matching rule's condition is not met by the visitor's
47
+ effective permissions.
48
+
49
+ `category` takes the same conditions as scripts:
50
+
51
+ ```ts
52
+ { category: 'measurement' }
53
+ { category: { and: ['measurement', 'marketing'] } }
54
+ { category: { or: ['measurement', 'marketing'] } }
55
+ ```
56
+
57
+ Add `vendor` to also block the request while the visitor has turned that
58
+ vendor off. IAB TCF rules use `vendorId` and the `iab*` purpose fields
59
+ instead.
60
+
61
+ | Option | Default | Purpose |
62
+ | -------------------- | -------- | ------------------------------------------------------------ |
63
+ | `rules` | required | Rules described above |
64
+ | `enabled` | `true` | Set `false` to keep the rules but stop blocking |
65
+ | `logBlockedRequests` | `true` | Log each blocked request with `console.warn` |
66
+ | `onRequestBlocked` | none | Called with `{ method, url, rule }` for each blocked request |
67
+
68
+ ## What a blocked request looks like
69
+
70
+ The blocker wraps `window.fetch` and `XMLHttpRequest`. A blocked `fetch`
71
+ resolves to a `451` response with the status text
72
+ `Request blocked by consent`, and nothing is sent. A blocked XHR is aborted
73
+ and fires an `error` event. Requests that match no rule are not delayed.
74
+
75
+ ## When blocking starts
76
+
77
+ The provider holds matching requests from its first render in the browser,
78
+ before any of its children render or run effects. That covers requests from
79
+ child components, including their mount effects, and from effects in
80
+ components rendered next to the provider. The blocker module itself loads
81
+ after mount and decides each held request. Apps without `networkBlocker` do
82
+ not download it.
83
+
84
+ The standalone `useNetworkBlocker` hook works the same way from the first
85
+ render of the component that calls it. That render patches `fetch` and
86
+ `XMLHttpRequest`. If React throws the render away and never commits it, the
87
+ hold ends after 10 seconds. Nothing checked consent for the requests it held,
88
+ so they fail the way the blocker fails a blocked request: a 451 response for
89
+ `fetch`, a failed XHR. The same happens when the component unmounts before
90
+ the blocker loads.
91
+
92
+ While consent is unknown, a matching request that would be blocked waits
93
+ instead of failing. Consent is unknown until the policy has loaded, which is
94
+ also when a returning visitor's stored choice takes effect. The request is
95
+ then sent if the choice allows it and blocked otherwise. If the policy fails
96
+ to load, optional categories stay denied and waiting requests are blocked.
97
+ If the policy request never finishes, they keep waiting and are never sent.
98
+
99
+ A synchronous XHR cannot wait. Before the blocker module has loaded, a
100
+ matching one throws a `NetworkError` from `send()`. After that, one that
101
+ consent does not allow yet is blocked.
102
+
103
+ ## What the network blocker cannot stop
104
+
105
+ The blocker only sees requests made after the provider starts rendering in
106
+ the browser, through `fetch` or `XMLHttpRequest`. It cannot stop:
107
+
108
+ * Code that runs before the provider renders: inline scripts in the HTML,
109
+ third-party tags in `<head>`, scripts loaded before hydration (such as
110
+ `next/script` with `beforeInteractive`), and client modules that evaluate
111
+ earlier. Webpack builds evaluate a route's client component modules when
112
+ its chunk loads, so their top-level code runs first. Turbopack evaluates a
113
+ client component module when its first element renders, which inside the
114
+ provider is after blocking starts.
115
+ * Code that saved its own reference to `fetch` or `XMLHttpRequest` before the
116
+ provider rendered.
117
+ * `navigator.sendBeacon`, `WebSocket`, `EventSource`, and requests made by
118
+ `<img>`, `<script>` or `<iframe>` elements, web workers and service
119
+ workers.
120
+ * With the standalone `useNetworkBlocker` hook instead of the provider
121
+ option, requests sent before the component that calls it renders.
122
+ Blocking starts in that component's first render, not the provider's.
123
+
124
+ Keep tracking calls out of that window:
125
+
126
+ * Send them from an effect or an event handler, never at module top level.
127
+ * Load vendor SDKs through `scripts` instead of a `<script>` tag or
128
+ `next/script`, so they wait for consent before they run at all.
129
+ * Check `useConsent('measurement')` (or the category you need) before you
130
+ call a vendor from your own code, and treat the blocker as a backstop.
131
+
132
+ ## Verify the blocked requests
133
+
134
+ Open DevTools Network in a private window, under a policy that asks for
135
+ consent.
136
+
137
+ 1. Before a choice, requests matching a rule are absent, and the console logs
138
+ each blocked request.
139
+ 2. Allow the rule's category and save. Matching requests go out.
140
+ 3. Reject, reload, and check that they stay absent.
@@ -0,0 +1,115 @@
1
+ ---
2
+ title: Scripts
3
+ description: Load vendor scripts by consent category in a React app with
4
+ ConsentProvider, and clear stored data or reload the page when a visitor
5
+ withdraws consent.
6
+ group: frameworks
7
+ ---
8
+
9
+ ## Register vendor scripts
10
+
11
+ Pass vendor loaders to `ConsentProvider` through `options.scripts`. The
12
+ provider loads each script when its category is allowed and removes it when
13
+ the category is denied. The [quickstart](https://c15t.com/docs/frameworks/react/quickstart)
14
+ registers PostHog and X Pixel this way:
15
+
16
+ ```ts title="src/scripts.ts"
17
+ import { posthog } from '@c15t/integrations/posthog';
18
+ import { xPixel } from '@c15t/integrations/x-pixel';
19
+
20
+ export const scripts = [
21
+ posthog({
22
+ id: 'phc_your_project_key',
23
+ initOptions: { cookieless_mode: 'never' },
24
+ loadMode: 'after-consent',
25
+ }),
26
+ xPixel({ pixelId: 'your-pixel-id' }),
27
+ ];
28
+ ```
29
+
30
+ The provider in `src/consent.tsx` receives them:
31
+
32
+ ```tsx title="src/consent.tsx"
33
+ import {
34
+ ConsentBanner,
35
+ ConsentDialog,
36
+ ConsentDialogLink,
37
+ ConsentProvider,
38
+ hosted,
39
+ } from 'c15t/react';
40
+ import type { ReactNode } from 'react';
41
+
42
+ import { scripts } from './scripts';
43
+
44
+ import 'c15t/react/styles.css';
45
+
46
+ const mode = hosted({ url: 'https://your-project.inth.app' });
47
+
48
+ export const Consent = ({ children }: { children: ReactNode }) => (
49
+ <ConsentProvider options={{ mode, scripts }}>
50
+ {children}
51
+ <ConsentBanner />
52
+ <ConsentDialog />
53
+ <footer>
54
+ <ConsentDialogLink>Privacy settings</ConsentDialogLink>
55
+ </footer>
56
+ </ConsentProvider>
57
+ );
58
+ ```
59
+
60
+ Each helper from `@c15t/integrations` sets its own category and a stable `id`.
61
+ Remove every other loader for the same vendor, such as a `<script>` tag in
62
+ `index.html` or an SDK you initialize at module level, so the vendor loads
63
+ once and only through c15t. [Integrations](../../integrations/overview.md) lists
64
+ every helper, and [building integrations](../../integrations/building-integrations.md)
65
+ covers a vendor without one.
66
+
67
+ ## What happens when consent changes
68
+
69
+ * **Allowed.** The script loads, or its SDK is started, the first time its
70
+ category is allowed.
71
+ * **Denied later.** The provider removes the script element. Code that already
72
+ ran keeps running, so after the visitor turns off a category they had
73
+ allowed, the provider reloads the page once the save finishes. Set
74
+ `reloadOnConsentRevoked: false` only if every gated vendor stops itself.
75
+ * **`alwaysLoad` helpers.** Some integrations, such as Google Consent Mode,
76
+ load before consent and pass the visitor's choice to the vendor. Read the
77
+ vendor's guide; a category on a script does not always mean zero requests.
78
+
79
+ ## Embeds and other requests
80
+
81
+ Scripts cover vendor code c15t loads for you. For the rest:
82
+
83
+ * [Embeds](./embeds.md) keeps iframes out of the page with
84
+ `ConsentGate` or the iframe blocker.
85
+ * [Network blocker](./network-blocker.md) holds `fetch` and
86
+ XHR calls that match a rule until their category is allowed.
87
+
88
+ ## Clear stored data after revocation
89
+
90
+ Removing a script does not delete the cookies or storage entries it wrote. Add
91
+ `clearOnRevocation` to the provider options to delete the entries you list for
92
+ each category when it is denied. The option is read once, when the provider
93
+ mounts. [Clear on revocation](https://c15t.com/docs/frameworks/react/clear-on-revocation) covers
94
+ configuration and browser limits.
95
+
96
+ ## Let visitors turn off one vendor
97
+
98
+ A visitor can allow marketing and still switch off one vendor in it. Pass
99
+ `vendors` to the provider options. Helpers from `@c15t/integrations` already carry
100
+ their vendor slug. See [vendor consent](https://c15t.com/docs/frameworks/react/vendor-consent).
101
+
102
+ To send your own events only to allowed integrations, see
103
+ [send events only to allowed integrations](../../integrations/overview.md#send-events-only-to-allowed-integrations).
104
+
105
+ ## Check the scripts
106
+
107
+ Open DevTools Network in a private window, filter by each vendor's domain and
108
+ reload.
109
+
110
+ 1. Before a choice under an opt-in policy, no vendor script loads.
111
+ 2. Allow one category. Only that category's vendors load.
112
+ 3. Turn the category off. The page reloads and the vendor stays absent.
113
+ 4. Reload again. The rejection holds.
114
+
115
+ [Verify consent](../../guides/verify-consent.md) has the full checklist.
@@ -0,0 +1,96 @@
1
+ ---
2
+ title: Embeds
3
+ description: Keep YouTube videos, maps and other iframes out of a Svelte page
4
+ until their consent category is allowed, with ConsentGate or the iframe
5
+ blocker.
6
+ group: frameworks
7
+ ---
8
+
9
+ ## Pick a way to gate the embed
10
+
11
+ A YouTube video, a map or a social post in an iframe contacts its vendor as
12
+ soon as it loads. c15t offers two ways to keep it from loading before
13
+ consent:
14
+
15
+ | Your markup | Use | Placeholder |
16
+ | -------------------------------------------------------- | ------------------------------------------------------ | ----------------------------- |
17
+ | A Svelte component you write | `ConsentGate` around the iframe | Built in, or your own snippet |
18
+ | HTML you do not control, such as CMS or Markdown content | The iframe blocker with `data-category` and `data-src` | None; the iframe stays empty |
19
+
20
+ Both use the visitor's effective permission for one category, and both
21
+ remove the embed again when the visitor withdraws that category.
22
+
23
+ ## Wrap the iframe in ConsentGate
24
+
25
+ ```svelte title="src/YouTubeEmbed.svelte"
26
+ <script lang="ts">
27
+ import { ConsentDialogLink, ConsentGate } from '@c15t/svelte';
28
+ </script>
29
+
30
+ <!-- The iframe mounts only while measurement is allowed. -->
31
+ <ConsentGate category="measurement">
32
+ {#snippet placeholder()}<div class="placeholder">
33
+ <p>Allow measurement to load this YouTube video.</p>
34
+ <ConsentDialogLink>Choose video permissions</ConsentDialogLink>
35
+ </div>{/snippet}
36
+ <iframe
37
+ title="YouTube video"
38
+ src="https://www.youtube-nocookie.com/embed/czTksCF6X8Y?playsinline=1"
39
+ allow="encrypted-media; picture-in-picture"
40
+ allowfullscreen
41
+ ></iframe>
42
+ </ConsentGate>
43
+ ```
44
+
45
+ The iframe is absent from the DOM until measurement is allowed, so the
46
+ browser never requests it. The placeholder snippet replaces the built-in one
47
+ and includes a `ConsentDialogLink`, so a visitor can allow the category from
48
+ the embed's own spot. Keep `ConsentDialog` mounted for that link.
49
+ [ConsentGate](https://c15t.com/docs/frameworks/svelte/components/consent-gate) lists its props.
50
+
51
+ Pick the category the vendor's embed needs. YouTube and maps usually fit
52
+ `measurement` or `marketing`; check what each vendor sets.
53
+
54
+ ## Gate iframes you do not render
55
+
56
+ The provider's iframe blocker is on by default. It watches the page for
57
+ iframes with a `data-category` attribute:
58
+
59
+ ```html
60
+ <iframe
61
+ title="Store locations"
62
+ data-category="functionality"
63
+ data-src="https://www.google.com/maps/embed?pb=..."
64
+ ></iframe>
65
+ ```
66
+
67
+ * While the category is denied, the iframe has no `src`, so it loads nothing.
68
+ * When the category is allowed, the blocker copies `data-src` to `src`.
69
+ Only `http` and `https` URLs are used.
70
+ * When the category is withdrawn, the blocker removes `src` again.
71
+
72
+ Iframes without `data-category` or `data-vendor` are never touched. Put
73
+ `data-src` in the HTML instead of `src`; an iframe that arrives with `src`
74
+ already starts loading before the blocker sees it. Add `data-vendor` with a
75
+ vendor slug to also keep the iframe empty while the visitor has that vendor
76
+ turned off.
77
+
78
+ The blocker has no placeholder. Style the empty iframe, or place a note and a
79
+ `ConsentDialogLink` next to it.
80
+
81
+ Pass `iframeBlocker={false}` on the provider to turn the blocker off.
82
+
83
+ ## Vendor guides
84
+
85
+ The [YouTube](../../integrations/youtube.md) and
86
+ [Google Maps](../../integrations/google-maps.md) guides have ready embed
87
+ configurations. [Integrations](../../integrations/overview.md) lists the rest.
88
+
89
+ ## Verify the embeds
90
+
91
+ Clear site data and reload with DevTools open:
92
+
93
+ 1. The Network panel has no request to the embed's host, and the Elements
94
+ panel shows the placeholder or an iframe without `src`.
95
+ 2. Allow the category in preferences. The embed loads without a page reload.
96
+ 3. Withdraw the category. The embed disappears, or its iframe loses `src`.