@c15t/scripts 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.
Files changed (300) hide show
  1. package/AGENTS.md +129 -59
  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 -137
  13. package/dist/engine/compile.js +2 -89
  14. package/dist/engine/runtime.js +2 -448
  15. package/dist/events.js +2 -0
  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 -392
  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 -0
  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 -66
  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 -0
  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 -56
  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 -0
  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 -59
  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 -0
  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 -458
  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 -0
  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 -93
  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 -0
  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 -158
  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 -0
  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 -94
  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 -59
  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/concepts/consent-state.md +357 -0
  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 -33
  207. package/docs/customization/recipes.md +839 -47
  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 +166 -36
  212. package/docs/customization/translations.md +61 -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 +146 -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 +119 -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 +137 -0
  232. package/docs/frameworks/sveltekit/embeds.md +103 -0
  233. package/docs/frameworks/sveltekit/network-blocker.md +149 -0
  234. package/docs/frameworks/sveltekit/scripts.md +141 -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 +168 -160
  245. package/docs/integrations/ahrefs-analytics.md +153 -155
  246. package/docs/integrations/amplitude.md +163 -157
  247. package/docs/integrations/building-integrations.md +136 -37
  248. package/docs/integrations/clearbit.md +155 -155
  249. package/docs/integrations/cloudflare-web-analytics.md +157 -157
  250. package/docs/integrations/cloudflare-zaraz.md +210 -262
  251. package/docs/integrations/crisp.md +165 -159
  252. package/docs/integrations/databuddy.md +158 -174
  253. package/docs/integrations/fathom-analytics.md +159 -157
  254. package/docs/integrations/front-chat.md +322 -0
  255. package/docs/integrations/google-maps.md +119 -84
  256. package/docs/integrations/google-tag-manager.md +179 -164
  257. package/docs/integrations/google-tag.md +164 -161
  258. package/docs/integrations/heap.md +164 -156
  259. package/docs/integrations/hightouch.md +162 -158
  260. package/docs/integrations/hotjar.md +160 -156
  261. package/docs/integrations/intercom.md +184 -154
  262. package/docs/integrations/klaviyo.md +486 -0
  263. package/docs/integrations/linkedin-insights.md +175 -151
  264. package/docs/integrations/logrocket.md +161 -157
  265. package/docs/integrations/matomo-analytics.md +189 -179
  266. package/docs/integrations/meta-pixel.md +189 -151
  267. package/docs/integrations/microsoft-clarity.md +164 -156
  268. package/docs/integrations/microsoft-uet.md +149 -155
  269. package/docs/integrations/mixpanel-analytics.md +156 -161
  270. package/docs/integrations/one-dollar-stats.md +306 -0
  271. package/docs/integrations/openai-pixel.md +205 -302
  272. package/docs/integrations/overview.md +143 -83
  273. package/docs/integrations/pinterest-tag.md +329 -0
  274. package/docs/integrations/pirsch.md +170 -160
  275. package/docs/integrations/plausible-analytics.md +173 -159
  276. package/docs/integrations/posthog.md +227 -242
  277. package/docs/integrations/promptwatch.md +155 -155
  278. package/docs/integrations/reddit-pixel.md +186 -158
  279. package/docs/integrations/rudderstack.md +202 -187
  280. package/docs/integrations/rybbit-analytics.md +172 -161
  281. package/docs/integrations/segment.md +183 -155
  282. package/docs/integrations/snapchat-pixel.md +187 -157
  283. package/docs/integrations/tiktok-pixel.md +172 -151
  284. package/docs/integrations/umami-analytics.md +164 -159
  285. package/docs/integrations/vercel-analytics.md +167 -158
  286. package/docs/integrations/x-pixel.md +177 -151
  287. package/docs/integrations/youtube.md +122 -87
  288. package/docs/upgrade-v3.md +490 -354
  289. package/package.json +12 -236
  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 -94
  295. package/docs/frameworks/next/script-loader.md +0 -210
  296. package/docs/frameworks/react/script-loader.md +0 -63
  297. package/docs/guides/consent-state.md +0 -60
  298. package/docs/guides/deployment-modes.md +0 -75
  299. package/docs/integrations/clear-on-revocation.md +0 -167
  300. package/docs/integrations/granular-consent.md +0 -208
@@ -0,0 +1,322 @@
1
+ ---
2
+ title: Front Chat
3
+ description: Load the Front Chat widget only after functionality consent with
4
+ the c15t frontChat helper, forward CSP nonces, clear the session on revocation
5
+ and check it in DevTools.
6
+ icon: front-chat
7
+ group: integrations
8
+ ---
9
+
10
+ ## Configure Front Chat
11
+
12
+ Copy the `chatId` from the widget snippet in your Front Chat channel's
13
+ **Installation** tab.
14
+
15
+ | Package manager | Command |
16
+ | :-------------- | :------------------------------------- |
17
+ | npm | `npm install @c15t/integrations@alpha` |
18
+ | pnpm | `pnpm add @c15t/integrations@alpha` |
19
+ | yarn | `yarn add @c15t/integrations@alpha` |
20
+ | bun | `bun add @c15t/integrations@alpha` |
21
+
22
+ ```ts title="src/consent-scripts.ts"
23
+ import { frontChat } from '@c15t/integrations/front-chat';
24
+
25
+ export const scripts = [frontChat({ chatId: 'YOUR_FRONT_CHAT_ID' })];
26
+ ```
27
+
28
+ ## Register the scripts
29
+
30
+ Complete your [framework quickstart](https://c15t.com/docs/frameworks) first. Keep its Inth
31
+ endpoint, policy, styles and consent UI. Remove the vendor's original script,
32
+ SDK initializer or tag-manager entry, so the vendor loads only through c15t.
33
+
34
+ The vendor pages put the helper in `src/consent-scripts.ts`. If your framework
35
+ quickstart already created a scripts file, such as `lib/scripts.ts` in the
36
+ Next.js guide, add the helper to that array instead of creating a second file.
37
+ The `scripts` export is a configuration, not an initializer. Add it to the c15t provider you already have, at the registration
38
+ point for your framework below. These are edits to that provider, not a second
39
+ provider.
40
+
41
+ **Next.js**
42
+
43
+ Import the configuration into the client boundary from your router guide:
44
+
45
+ ```ts
46
+ import { ConsentRoot } from 'c15t/next';
47
+ import { scripts } from './consent-scripts';
48
+ ```
49
+
50
+ Keep the server-resolved `state` and shared `consentConfig` from your
51
+ router guide. Its manifest, init and save URLs stay in effect. Add
52
+ `scripts` as a top-level prop on the existing root:
53
+
54
+ ```tsx
55
+ <ConsentRoot state={state} config={consentConfig} scripts={scripts}>
56
+ {children}
57
+ </ConsentRoot>
58
+ ```
59
+
60
+ App Router, Pages Router and static export all use this `ConsentRoot` in
61
+ the `'use client'` wrapper `components/consent.tsx`. Keep `scripts` there,
62
+ because a Server Component cannot pass script callbacks to it. See
63
+ [Next.js scripts and embeds](../frameworks/next/scripts.md).
64
+
65
+ **TanStack Start**
66
+
67
+ Import the configuration into your root route and pass it to the existing
68
+ `ConsentRoot` as a top-level prop. Keep the loader, `backendURL` and
69
+ `initRoute` from the [TanStack Start quickstart](https://c15t.com/docs/frameworks/tanstack-start/quickstart):
70
+
71
+ ```tsx title="src/routes/__root.tsx"
72
+ import { scripts } from '../consent-scripts';
73
+
74
+ <ConsentRoot
75
+ state={consent}
76
+ backendURL={backendURL}
77
+ initRoute={false}
78
+ scripts={scripts}
79
+ >
80
+ ```
81
+
82
+ Import vendor helpers in the root route module, not in a server function.
83
+ A server function's return value must be serializable, and script
84
+ configurations carry callbacks. See
85
+ [TanStack Start scripts](../frameworks/tanstack-start/scripts.md).
86
+
87
+ **React**
88
+
89
+ Add the configuration to the existing `ConsentProvider` options, next to
90
+ `mode`:
91
+
92
+ ```tsx title="src/consent.tsx"
93
+ import { scripts } from './consent-scripts';
94
+
95
+ <ConsentProvider options={{ mode, scripts }}>
96
+ ```
97
+
98
+ `mode` is the `hosted({ url: 'https://your-project.inth.app' })` value
99
+ from the [React quickstart](https://c15t.com/docs/frameworks/react/quickstart). Keep the banner,
100
+ dialog and preferences link inside the provider. See
101
+ [React scripts and embeds](../frameworks/react/scripts.md).
102
+
103
+ **Nuxt**
104
+
105
+ Register the scripts under the `c15t` key in `app/app.config.ts`. Adjust the
106
+ relative import to where you created `consent-scripts.ts`:
107
+
108
+ ```ts title="app/app.config.ts"
109
+ import { scripts } from '../src/consent-scripts';
110
+
111
+ export default defineAppConfig({
112
+ c15t: { scripts },
113
+ });
114
+ ```
115
+
116
+ The Nuxt module merges this over its options in `nuxt.config.ts` and starts
117
+ one script loader in the browser after hydration, once it has applied the
118
+ visitor's stored choice and privacy signals. Keep `scripts` out of
119
+ `nuxt.config.ts`, which reaches the browser as JSON and drops the vendor
120
+ callbacks. Write the vendor IDs into `consent-scripts.ts`. See
121
+ [Nuxt scripts and embeds](../frameworks/nuxt/scripts.md).
122
+
123
+ **Vue**
124
+
125
+ Pass the scripts to the existing `c15tVue` plugin call in `src/main.ts`:
126
+
127
+ ```ts title="src/main.ts"
128
+ import { scripts } from './consent-scripts';
129
+
130
+ app.use(c15tVue, {
131
+ backendURL: 'https://your-project.inth.app',
132
+ scripts,
133
+ });
134
+ ```
135
+
136
+ Keep your existing backend URL and other options. The plugin starts one
137
+ script loader when the app mounts, after it has applied the visitor's stored
138
+ choice. Do not also call `createScriptLoader` from a component. See
139
+ [Vue scripts and embeds](../frameworks/vue/scripts.md).
140
+
141
+ **Astro**
142
+
143
+ Add the scripts to the client entrypoint from the
144
+ [Astro quickstart](https://c15t.com/docs/frameworks/astro/quickstart), the module that the
145
+ integration's `clientEntrypoint` option names. Keep `mode`, `ui` and the
146
+ framework integration in `astro.config.mjs` as they are. If the module
147
+ already exports scripts, combine the two arrays.
148
+
149
+ ```ts title="src/consent-client.ts"
150
+ import type { C15tClientOptionsExtension } from 'c15t/astro';
151
+ import { scripts } from './consent-scripts';
152
+
153
+ export default { scripts } satisfies C15tClientOptionsExtension;
154
+ ```
155
+
156
+ Vendor helpers contain callbacks, and the integration options in
157
+ `astro.config.mjs` are serialized into the page, so do not put helpers in
158
+ the integration's `scripts` option. The integration passes the client
159
+ entrypoint to the one runtime every page shares, including across
160
+ `ClientRouter` navigation.
161
+
162
+ **Svelte**
163
+
164
+ Import the scripts in the component that owns your existing provider and
165
+ pass them as a top-level prop:
166
+
167
+ ```svelte title="src/App.svelte"
168
+ <script lang="ts">
169
+ import { ConsentManagerProvider, hosted } from '@c15t/svelte';
170
+ import { scripts } from './consent-scripts';
171
+
172
+ const mode = hosted({ url: 'https://your-project.inth.app' });
173
+ </script>
174
+
175
+ <ConsentManagerProvider {mode} {scripts}>
176
+ <!-- Keep your application, consent UI and preferences link here. -->
177
+ </ConsentManagerProvider>
178
+ ```
179
+
180
+ Retain the styles and consent UI from the [Svelte quickstart](https://c15t.com/docs/frameworks/svelte/quickstart).
181
+ The provider owns the loader and disposes it on unmount.
182
+
183
+ **SvelteKit**
184
+
185
+ Add the scripts to the existing root layout provider. Keep the server load
186
+ and its serializable prefetch data from the [SvelteKit quickstart](https://c15t.com/docs/frameworks/sveltekit/quickstart).
187
+
188
+ ```svelte title="src/routes/+layout.svelte"
189
+ <script lang="ts">
190
+ import { ConsentManagerProvider, hosted } from '@c15t/svelte';
191
+ import { scripts } from '../consent-scripts';
192
+
193
+ let { children, data } = $props();
194
+ const mode = hosted({ url: 'https://your-project.inth.app' });
195
+ </script>
196
+
197
+ <ConsentManagerProvider {mode} {scripts} prefetch={data.prefetch}>
198
+ {@render children()}
199
+ <!-- Keep your consent UI and preferences link here. -->
200
+ </ConsentManagerProvider>
201
+ ```
202
+
203
+ Import vendor helpers in the layout component, not in `+layout.server.ts`:
204
+ a server load cannot send functions to the browser. Prerendered, static and
205
+ SPA-mode pages use the same `scripts` prop. If you pass an externally owned
206
+ `runtime` to the provider, register scripts when creating that runtime instead.
207
+
208
+ **HTML**
209
+
210
+ The helpers in `@c15t/integrations` are ES modules that need a bundler. On a
211
+ page that loads the c15t script tag, paste the vendor's own snippet instead
212
+ and keep it inert until its category is allowed:
213
+
214
+ ```html
215
+ <script type="text/plain" data-c15t-category="measurement">
216
+ // The vendor's snippet, unchanged
217
+ </script>
218
+ ```
219
+
220
+ Use the category this guide names for the vendor. c15t runs the snippet
221
+ once that category is allowed, and reloads the page when the visitor
222
+ withdraws it. Helper options on this page, such as `loadMode`, do not apply
223
+ to a pasted snippet. See [HTML scripts](../frameworks/html/scripts.md).
224
+
225
+ **JavaScript**
226
+
227
+ Pass the scripts to `init()` from `@c15t/browser`, next to your backend
228
+ URL:
229
+
230
+ ```ts
231
+ import { init } from '@c15t/browser';
232
+ import { scripts } from './consent-scripts';
233
+
234
+ const consent = init({
235
+ backendURL: 'https://your-project.inth.app',
236
+ scripts,
237
+ });
238
+ ```
239
+
240
+ Keep the backend URL from your quickstart. With
241
+ `createConsentRuntime` from `c15t/runtime`, pass `scripts` to it instead.
242
+ A kernel you create yourself needs a loader from
243
+ `c15t/modules/script-loader`. Attach one loader per kernel. See
244
+ [JavaScript scripts](../frameworks/javascript/scripts.md).
245
+
246
+ ## Options
247
+
248
+ | Option | Default | Behavior |
249
+ | -------------------- | ---------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
250
+ | `chatId` | Required | Public chat ID passed to `FrontChat('init')`. Surrounding whitespace is trimmed; an empty value throws. |
251
+ | `useDefaultLauncher` | `true` | Shows Front's launcher. With `false`, open the widget from your own launcher through Front's [SDK](https://dev.frontapp.com/docs/chat-sdk-reference). |
252
+ | `nonce` | None | CSP nonce for the loader and Front's generated scripts. A loader nonce from your consent provider is forwarded to `init` too. |
253
+ | `scriptSrc` | `https://chat-assets.frontapp.com/v1/chat.bundle.js` | Loader URL override. A proxy here does not proxy the widget's other requests. |
254
+
255
+ Front creates more resources for the widget. Follow Front's
256
+ [CSP instructions](https://help.front.com/en/articles/2049) for the rest of the
257
+ policy.
258
+
259
+ ## Loading and revocation
260
+
261
+ `frontChat` uses the `functionality` category and appends its loader to
262
+ `<body>` once functionality is allowed. After the SDK loads, the helper calls
263
+ `FrontChat('init')` with your chat ID. If functionality is revoked while the
264
+ SDK is still downloading, the helper skips `init`.
265
+
266
+ On revocation the helper removes the loader element and calls no Front API.
267
+ The running widget stays until c15t reloads the page, so keep
268
+ `reloadOnConsentRevoked` enabled.
269
+
270
+ ## Clear the Front session on revocation
271
+
272
+ The reload removes the widget but keeps the visitor's Front session. To clear
273
+ it, call `shutdownFrontChat()` from the `onBeforeConsentRevocationReload`
274
+ callback in your consent options:
275
+
276
+ ```ts title="src/consent-callbacks.ts"
277
+ import { shutdownFrontChat } from '@c15t/integrations/front-chat';
278
+
279
+ export const callbacks = {
280
+ onBeforeConsentRevocationReload: ({ preferences }) => {
281
+ if (!preferences.functionality) {
282
+ shutdownFrontChat();
283
+ }
284
+ },
285
+ };
286
+ ```
287
+
288
+ `shutdownFrontChat` sends `FrontChat('shutdown', { clearSession: true })` when
289
+ the SDK has loaded and does nothing otherwise. Front returns no completion
290
+ signal, so the reload does not wait for the session to clear.
291
+
292
+ ## Verify Front Chat
293
+
294
+ After you allow functionality, `chat.bundle.js` loads and the widget connects
295
+ to your Front inbox. With `shutdownFrontChat` wired, revoke functionality and
296
+ check that the next grant starts a new Front session.
297
+
298
+ Test in a private window with an opt-in policy. Open DevTools Network, disable
299
+ the cache and filter by the vendor's domain:
300
+
301
+ 1. Load the page. No request goes to the vendor before you choose.
302
+ 2. Click Reject, then reload. There is still no vendor request.
303
+ 3. Open Privacy settings and allow the helper's category. The vendor script
304
+ loads without a page reload.
305
+ 4. Turn the category off again and save. c15t reloads the page, and the new
306
+ page makes no vendor request.
307
+
308
+ c15t reloads on revocation because removing a script element does not stop
309
+ code that already ran. The vendor's listeners, timers and queued events stay
310
+ alive until the page unloads. If you set `reloadOnConsentRevoked: false`, stop
311
+ the vendor yourself. Register a callback-only script whose `onConsentChange`
312
+ calls the vendor's opt-out API, as shown in
313
+ [custom integrations](./building-integrations.md), and check the
314
+ permission before each of your own event calls. The reload does not delete
315
+ cookies the vendor already set; see
316
+ [clear on revocation for your framework](./overview.md#vendor-switches-and-cookie-cleanup).
317
+
318
+ The helper sets `vendor` to its script ID, so once you declare that vendor a
319
+ visitor can turn it off inside an allowed category. See
320
+ [vendor consent for your framework](./overview.md#vendor-switches-and-cookie-cleanup). The
321
+ [consent verification guide](../guides/verify-consent.md) covers navigation,
322
+ expiry and hosting checks.
@@ -1,6 +1,9 @@
1
1
  ---
2
2
  title: Google Maps
3
- description: Prevent a map iframe from mounting before the required permission.
3
+ description: Gate a Google Maps iframe embed with c15t v3 so the map loads only
4
+ after the visitor allows its consent category, in Next.js, TanStack Start,
5
+ React, Nuxt, Vue, Astro, Svelte, SvelteKit, HTML or JavaScript.
6
+ icon: google-maps
4
7
  group: integrations
5
8
  ---
6
9
 
@@ -11,7 +14,8 @@ application; this example uses functionality permission.
11
14
 
12
15
  ```ts title="src/embed-config.ts"
13
16
  export const embedCategory = 'functionality' as const;
14
- export const embedURL = 'https://www.google.com/maps/embed?pb=YOUR_EMBED_PARAMETERS';
17
+ export const embedURL =
18
+ 'https://www.google.com/maps/embed?pb=YOUR_EMBED_PARAMETERS';
15
19
  export const embedTitle = 'Map showing our office location';
16
20
  export const embedAspectRatio = '4 / 3';
17
21
  ```
@@ -107,64 +111,66 @@ on revocation. Keep your existing consent styles and preferences dialog.
107
111
 
108
112
  **Nuxt**
109
113
 
110
- Use the reactive snapshot from the existing consent runtime. The Nuxt module auto-imports the consent composables.
114
+ The Nuxt module registers `ConsentGate` and `ConsentPreferencesLink`, so
115
+ this component imports only the embed configuration.
111
116
 
112
117
  ```vue title="app/components/ConsentEmbed.vue"
113
118
  <script setup lang="ts">
114
119
  import { embedCategory, embedURL, embedTitle, embedAspectRatio } from '../../src/embed-config';
115
-
116
- const snapshot = useConsentSnapshot();
117
- const activeUI = useConsentActiveUI();
118
120
  </script>
119
121
 
120
122
  <template>
121
- <iframe
122
- v-if="snapshot.effectivePermissions[embedCategory]"
123
- :src="embedURL"
124
- :title="embedTitle"
125
- loading="lazy"
126
- allowfullscreen
127
- :style="{ width: '100%', aspectRatio: embedAspectRatio, minHeight: '200px', border: 0 }"
128
- />
129
- <button v-else type="button" @click="activeUI = 'manager'">
130
- Open privacy settings to view this content
131
- </button>
123
+ <ConsentGate :category="embedCategory">
124
+ <iframe
125
+ :src="embedURL"
126
+ :title="embedTitle"
127
+ loading="lazy"
128
+ allowfullscreen
129
+ :style="{ width: '100%', aspectRatio: embedAspectRatio, minHeight: '200px', border: 0 }"
130
+ />
131
+ <template #placeholder>
132
+ <p>Allow {{ embedCategory }} to load this content.</p>
133
+ <ConsentPreferencesLink>Open privacy settings</ConsentPreferencesLink>
134
+ </template>
135
+ </ConsentGate>
132
136
  </template>
133
137
  ```
134
138
 
135
- Use `v-if` so a denied iframe is removed from the DOM. Hiding an existing
136
- iframe with `v-show` or CSS does not prevent its requests.
139
+ `ConsentGate` keeps the iframe out of the server HTML and the DOM while
140
+ permission is denied, and removes it on revocation. See
141
+ [Nuxt scripts and embeds](../frameworks/nuxt/scripts.md).
137
142
 
138
143
  **Vue**
139
144
 
140
- Use the reactive snapshot from the existing consent runtime. The c15t Vue plugin must already be installed on this app.
145
+ Render this component inside the app that installed the c15t Vue plugin.
141
146
 
142
147
  ```vue title="src/ConsentEmbed.vue"
143
148
  <script setup lang="ts">
144
- import { useConsentSnapshot, useConsentActiveUI } from 'c15t/vue/vue-plugin';
149
+ import ConsentGate from 'c15t/vue/runtime/components/consent-gate.vue';
150
+ import ConsentPreferencesLink from 'c15t/vue/runtime/components/consent-preferences-link.vue';
145
151
  import { embedCategory, embedURL, embedTitle, embedAspectRatio } from './embed-config';
146
-
147
- const snapshot = useConsentSnapshot();
148
- const activeUI = useConsentActiveUI();
149
152
  </script>
150
153
 
151
154
  <template>
152
- <iframe
153
- v-if="snapshot.effectivePermissions[embedCategory]"
154
- :src="embedURL"
155
- :title="embedTitle"
156
- loading="lazy"
157
- allowfullscreen
158
- :style="{ width: '100%', aspectRatio: embedAspectRatio, minHeight: '200px', border: 0 }"
159
- />
160
- <button v-else type="button" @click="activeUI = 'manager'">
161
- Open privacy settings to view this content
162
- </button>
155
+ <ConsentGate :category="embedCategory">
156
+ <iframe
157
+ :src="embedURL"
158
+ :title="embedTitle"
159
+ loading="lazy"
160
+ allowfullscreen
161
+ :style="{ width: '100%', aspectRatio: embedAspectRatio, minHeight: '200px', border: 0 }"
162
+ />
163
+ <template #placeholder>
164
+ <p>Allow {{ embedCategory }} to load this content.</p>
165
+ <ConsentPreferencesLink>Open privacy settings</ConsentPreferencesLink>
166
+ </template>
167
+ </ConsentGate>
163
168
  </template>
164
169
  ```
165
170
 
166
- Use `v-if` so a denied iframe is removed from the DOM. Hiding an existing
167
- iframe with `v-show` or CSS does not prevent its requests.
171
+ `ConsentGate` keeps the iframe out of the DOM while permission is denied,
172
+ and removes it on revocation. See
173
+ [Vue scripts and embeds](../frameworks/vue/scripts.md).
168
174
 
169
175
  **Astro**
170
176
 
@@ -175,7 +181,7 @@ Add this component to pages using your consent-enabled base layout:
175
181
  <c15t-consent-embed style="display: block"></c15t-consent-embed>
176
182
 
177
183
  <script>
178
- import { getConsentClient } from '@c15t/astro/client';
184
+ import { getConsentClient } from 'c15t/astro/client';
179
185
  import { mountConsentEmbed } from '../consent-embed';
180
186
 
181
187
  class ConsentEmbed extends HTMLElement {
@@ -273,16 +279,37 @@ The Svelte `ConsentGate` waits until the browser is mounted and the category is
273
279
  allowed. Its default placeholder opens preferences. Revocation removes the
274
280
  iframe.
275
281
 
282
+ **HTML**
283
+
284
+ The c15t script tag gates iframes that name a category. Put the embed's URL
285
+ in `data-src` instead of `src`, using the values from the configuration on
286
+ this page:
287
+
288
+ ```html
289
+ <iframe
290
+ data-src="https://www.youtube-nocookie.com/embed/VIDEO_ID?playsinline=1"
291
+ data-category="measurement"
292
+ title="Product video"
293
+ loading="lazy"
294
+ allowfullscreen
295
+ ></iframe>
296
+ ```
297
+
298
+ c15t sets `src` once the category is allowed and removes it on revocation.
299
+ Without `src` the iframe loads nothing, so hide it with CSS and show a link
300
+ to `#c15t-preferences` in its place. See
301
+ [HTML embeds](../frameworks/html/embeds.md).
302
+
276
303
  **JavaScript**
277
304
 
278
- Use the shared browser helper below with your existing kernel. Put an
305
+ Use the shared browser helper below with your existing client. Put an
279
306
  empty container where the embed should appear:
280
307
 
281
308
  ```html
282
309
  <div id="consent-embed"></div>
283
310
  ```
284
311
 
285
- In your browser entry point, after creating the kernel:
312
+ In your browser entry point, after `init()`:
286
313
 
287
314
  ```ts
288
315
  import { mountConsentEmbed } from './consent-embed';
@@ -290,13 +317,16 @@ import { mountConsentEmbed } from './consent-embed';
290
317
  const container = document.querySelector<HTMLElement>('#consent-embed');
291
318
  if (!container) throw new Error('Missing consent embed container');
292
319
 
293
- const disposeEmbed = mountConsentEmbed(container, kernel, openPreferences);
320
+ const disposeEmbed = mountConsentEmbed(container, consent.kernel, () =>
321
+ consent.openDialog(),
322
+ );
294
323
  ```
295
324
 
296
- `kernel` is the instance from your quickstart. `openPreferences` is your
297
- application's function for showing its consent preferences UI. Call
298
- `disposeEmbed()` when the page or component is destroyed. The helper observes
299
- both the current snapshot and later changes.
325
+ `consent` is the client from `init()` in your quickstart. With
326
+ `createConsentRuntime`, pass `runtime.kernel` and your own function that
327
+ opens preferences. Call `disposeEmbed()` when the page or component is
328
+ destroyed. Iframe markup with `data-src` and `data-category` also works
329
+ without the helper, because both setups gate iframes by default.
300
330
 
301
331
  ## Browser helper for Astro and JavaScript
302
332
 
@@ -305,51 +335,56 @@ when permission allows it, keeps an existing player mounted across unrelated
305
335
  snapshot updates, and removes it on revocation.
306
336
 
307
337
  ```ts title="src/consent-embed.ts"
308
- import { embedCategory, embedURL, embedTitle, embedAspectRatio } from './embed-config';
338
+ import {
339
+ embedCategory,
340
+ embedURL,
341
+ embedTitle,
342
+ embedAspectRatio,
343
+ } from './embed-config';
309
344
 
310
345
  type EmbedKernel = {
311
- getSnapshot: () => {
312
- effectivePermissions: Record<typeof embedCategory, boolean>;
313
- };
314
- subscribe: (listener: () => void) => () => void;
346
+ getSnapshot: () => {
347
+ effectivePermissions: Record<typeof embedCategory, boolean>;
348
+ };
349
+ subscribe: (listener: () => void) => () => void;
315
350
  };
316
351
 
317
352
  export function mountConsentEmbed(
318
- container: HTMLElement,
319
- kernel: EmbedKernel,
320
- openPreferences: () => void,
353
+ container: HTMLElement,
354
+ kernel: EmbedKernel,
355
+ openPreferences: () => void
321
356
  ) {
322
- const render = () => {
323
- if (kernel.getSnapshot().effectivePermissions[embedCategory]) {
324
- if (container.querySelector('iframe')) return;
325
- const frame = document.createElement('iframe');
326
- frame.src = embedURL;
327
- frame.title = embedTitle;
328
- frame.loading = 'lazy';
329
- frame.allowFullscreen = true;
330
- Object.assign(frame.style, {
331
- width: '100%',
332
- aspectRatio: embedAspectRatio,
333
- minHeight: '200px',
334
- border: '0',
335
- });
336
- container.replaceChildren(frame);
337
- } else {
338
- if (container.querySelector('button')) return;
339
- const button = document.createElement('button');
340
- button.type = 'button';
341
- button.textContent = 'Open privacy settings to view this content';
342
- button.onclick = openPreferences;
343
- container.replaceChildren(button);
344
- }
345
- };
346
-
347
- render();
348
- const unsubscribe = kernel.subscribe(render);
349
- return () => {
350
- unsubscribe();
351
- container.replaceChildren();
352
- };
357
+ const render = () => {
358
+ if (kernel.getSnapshot().effectivePermissions[embedCategory]) {
359
+ if (container.querySelector('iframe')) return;
360
+ const frame = document.createElement('iframe');
361
+ frame.src = embedURL;
362
+ frame.title = embedTitle;
363
+ frame.loading = 'lazy';
364
+ frame.allowFullscreen = true;
365
+ Object.assign(frame.style, {
366
+ width: '100%',
367
+ aspectRatio: embedAspectRatio,
368
+ minHeight: '200px',
369
+ border: '0',
370
+ });
371
+ container.replaceChildren(frame);
372
+ } else {
373
+ if (container.querySelector('button')) return;
374
+ const button = document.createElement('button');
375
+ button.type = 'button';
376
+ button.textContent = 'Open privacy settings to view this content';
377
+ button.onclick = openPreferences;
378
+ container.replaceChildren(button);
379
+ }
380
+ };
381
+
382
+ render();
383
+ const unsubscribe = kernel.subscribe(render);
384
+ return () => {
385
+ unsubscribe();
386
+ container.replaceChildren();
387
+ };
353
388
  }
354
389
  ```
355
390