@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
@@ -2,18 +2,22 @@
2
2
  title: YouTube
3
3
  description: Gate YouTube embeds with c15t v3 in Next.js, TanStack Start, React,
4
4
  Nuxt, Vue, Astro, Svelte, SvelteKit or JavaScript.
5
+ icon: youtube
5
6
  group: integrations
6
7
  ---
7
8
 
8
9
  ## Configure the video
9
10
 
10
11
  Use the ID of an embeddable video and the category matching its purpose in your
11
- policy. The current v3 adapters use consent-gated iframes rather than the v2
12
+ policy. This page uses `marketing` because the YouTube player can set
13
+ advertising identifiers. The framework quickstarts gate their demo video on
14
+ `measurement` instead; either works if your policy says so. The current v3 adapters use consent-gated iframes rather than the v2
12
15
  `YouTubeEmbed` convenience component.
13
16
 
14
17
  ```ts title="src/embed-config.ts"
15
18
  export const embedCategory = 'marketing' as const;
16
- export const embedURL = 'https://www.youtube-nocookie.com/embed/VIDEO_ID?playsinline=1';
19
+ export const embedURL =
20
+ 'https://www.youtube-nocookie.com/embed/VIDEO_ID?playsinline=1';
17
21
  export const embedTitle = 'Product walkthrough';
18
22
  export const embedAspectRatio = '16 / 9';
19
23
  ```
@@ -108,64 +112,66 @@ on revocation. Keep your existing consent styles and preferences dialog.
108
112
 
109
113
  **Nuxt**
110
114
 
111
- Use the reactive snapshot from the existing consent runtime. The Nuxt module auto-imports the consent composables.
115
+ The Nuxt module registers `ConsentGate` and `ConsentPreferencesLink`, so
116
+ this component imports only the embed configuration.
112
117
 
113
118
  ```vue title="app/components/ConsentEmbed.vue"
114
119
  <script setup lang="ts">
115
120
  import { embedCategory, embedURL, embedTitle, embedAspectRatio } from '../../src/embed-config';
116
-
117
- const snapshot = useConsentSnapshot();
118
- const activeUI = useConsentActiveUI();
119
121
  </script>
120
122
 
121
123
  <template>
122
- <iframe
123
- v-if="snapshot.effectivePermissions[embedCategory]"
124
- :src="embedURL"
125
- :title="embedTitle"
126
- loading="lazy"
127
- allowfullscreen
128
- :style="{ width: '100%', aspectRatio: embedAspectRatio, minHeight: '200px', border: 0 }"
129
- />
130
- <button v-else type="button" @click="activeUI = 'manager'">
131
- Open privacy settings to view this content
132
- </button>
124
+ <ConsentGate :category="embedCategory">
125
+ <iframe
126
+ :src="embedURL"
127
+ :title="embedTitle"
128
+ loading="lazy"
129
+ allowfullscreen
130
+ :style="{ width: '100%', aspectRatio: embedAspectRatio, minHeight: '200px', border: 0 }"
131
+ />
132
+ <template #placeholder>
133
+ <p>Allow {{ embedCategory }} to load this content.</p>
134
+ <ConsentPreferencesLink>Open privacy settings</ConsentPreferencesLink>
135
+ </template>
136
+ </ConsentGate>
133
137
  </template>
134
138
  ```
135
139
 
136
- Use `v-if` so a denied iframe is removed from the DOM. Hiding an existing
137
- iframe with `v-show` or CSS does not prevent its requests.
140
+ `ConsentGate` keeps the iframe out of the server HTML and the DOM while
141
+ permission is denied, and removes it on revocation. See
142
+ [Nuxt scripts and embeds](../frameworks/nuxt/scripts.md).
138
143
 
139
144
  **Vue**
140
145
 
141
- Use the reactive snapshot from the existing consent runtime. The c15t Vue plugin must already be installed on this app.
146
+ Render this component inside the app that installed the c15t Vue plugin.
142
147
 
143
148
  ```vue title="src/ConsentEmbed.vue"
144
149
  <script setup lang="ts">
145
- import { useConsentSnapshot, useConsentActiveUI } from 'c15t/vue/vue-plugin';
150
+ import ConsentGate from 'c15t/vue/runtime/components/consent-gate.vue';
151
+ import ConsentPreferencesLink from 'c15t/vue/runtime/components/consent-preferences-link.vue';
146
152
  import { embedCategory, embedURL, embedTitle, embedAspectRatio } from './embed-config';
147
-
148
- const snapshot = useConsentSnapshot();
149
- const activeUI = useConsentActiveUI();
150
153
  </script>
151
154
 
152
155
  <template>
153
- <iframe
154
- v-if="snapshot.effectivePermissions[embedCategory]"
155
- :src="embedURL"
156
- :title="embedTitle"
157
- loading="lazy"
158
- allowfullscreen
159
- :style="{ width: '100%', aspectRatio: embedAspectRatio, minHeight: '200px', border: 0 }"
160
- />
161
- <button v-else type="button" @click="activeUI = 'manager'">
162
- Open privacy settings to view this content
163
- </button>
156
+ <ConsentGate :category="embedCategory">
157
+ <iframe
158
+ :src="embedURL"
159
+ :title="embedTitle"
160
+ loading="lazy"
161
+ allowfullscreen
162
+ :style="{ width: '100%', aspectRatio: embedAspectRatio, minHeight: '200px', border: 0 }"
163
+ />
164
+ <template #placeholder>
165
+ <p>Allow {{ embedCategory }} to load this content.</p>
166
+ <ConsentPreferencesLink>Open privacy settings</ConsentPreferencesLink>
167
+ </template>
168
+ </ConsentGate>
164
169
  </template>
165
170
  ```
166
171
 
167
- Use `v-if` so a denied iframe is removed from the DOM. Hiding an existing
168
- iframe with `v-show` or CSS does not prevent its requests.
172
+ `ConsentGate` keeps the iframe out of the DOM while permission is denied,
173
+ and removes it on revocation. See
174
+ [Vue scripts and embeds](../frameworks/vue/scripts.md).
169
175
 
170
176
  **Astro**
171
177
 
@@ -274,16 +280,37 @@ The Svelte `ConsentGate` waits until the browser is mounted and the category is
274
280
  allowed. Its default placeholder opens preferences. Revocation removes the
275
281
  iframe.
276
282
 
283
+ **HTML**
284
+
285
+ The c15t script tag gates iframes that name a category. Put the embed's URL
286
+ in `data-src` instead of `src`, using the values from the configuration on
287
+ this page:
288
+
289
+ ```html
290
+ <iframe
291
+ data-src="https://www.youtube-nocookie.com/embed/VIDEO_ID?playsinline=1"
292
+ data-category="measurement"
293
+ title="Product video"
294
+ loading="lazy"
295
+ allowfullscreen
296
+ ></iframe>
297
+ ```
298
+
299
+ c15t sets `src` once the category is allowed and removes it on revocation.
300
+ Without `src` the iframe loads nothing, so hide it with CSS and show a link
301
+ to `#c15t-preferences` in its place. See
302
+ [HTML embeds](../frameworks/html/embeds.md).
303
+
277
304
  **JavaScript**
278
305
 
279
- Use the shared browser helper below with your existing kernel. Put an
306
+ Use the shared browser helper below with your existing client. Put an
280
307
  empty container where the embed should appear:
281
308
 
282
309
  ```html
283
310
  <div id="consent-embed"></div>
284
311
  ```
285
312
 
286
- In your browser entry point, after creating the kernel:
313
+ In your browser entry point, after `init()`:
287
314
 
288
315
  ```ts
289
316
  import { mountConsentEmbed } from './consent-embed';
@@ -291,13 +318,16 @@ import { mountConsentEmbed } from './consent-embed';
291
318
  const container = document.querySelector<HTMLElement>('#consent-embed');
292
319
  if (!container) throw new Error('Missing consent embed container');
293
320
 
294
- const disposeEmbed = mountConsentEmbed(container, kernel, openPreferences);
321
+ const disposeEmbed = mountConsentEmbed(container, consent.kernel, () =>
322
+ consent.openDialog(),
323
+ );
295
324
  ```
296
325
 
297
- `kernel` is the instance from your quickstart. `openPreferences` is your
298
- application's function for showing its consent preferences UI. Call
299
- `disposeEmbed()` when the page or component is destroyed. The helper observes
300
- both the current snapshot and later changes.
326
+ `consent` is the client from `init()` in your quickstart. With
327
+ `createConsentRuntime`, pass `runtime.kernel` and your own function that
328
+ opens preferences. Call `disposeEmbed()` when the page or component is
329
+ destroyed. Iframe markup with `data-src` and `data-category` also works
330
+ without the helper, because both setups gate iframes by default.
301
331
 
302
332
  ## Browser helper for Astro and JavaScript
303
333
 
@@ -306,51 +336,56 @@ when permission allows it, keeps an existing player mounted across unrelated
306
336
  snapshot updates, and removes it on revocation.
307
337
 
308
338
  ```ts title="src/consent-embed.ts"
309
- import { embedCategory, embedURL, embedTitle, embedAspectRatio } from './embed-config';
339
+ import {
340
+ embedCategory,
341
+ embedURL,
342
+ embedTitle,
343
+ embedAspectRatio,
344
+ } from './embed-config';
310
345
 
311
346
  type EmbedKernel = {
312
- getSnapshot: () => {
313
- effectivePermissions: Record<typeof embedCategory, boolean>;
314
- };
315
- subscribe: (listener: () => void) => () => void;
347
+ getSnapshot: () => {
348
+ effectivePermissions: Record<typeof embedCategory, boolean>;
349
+ };
350
+ subscribe: (listener: () => void) => () => void;
316
351
  };
317
352
 
318
353
  export function mountConsentEmbed(
319
- container: HTMLElement,
320
- kernel: EmbedKernel,
321
- openPreferences: () => void,
354
+ container: HTMLElement,
355
+ kernel: EmbedKernel,
356
+ openPreferences: () => void
322
357
  ) {
323
- const render = () => {
324
- if (kernel.getSnapshot().effectivePermissions[embedCategory]) {
325
- if (container.querySelector('iframe')) return;
326
- const frame = document.createElement('iframe');
327
- frame.src = embedURL;
328
- frame.title = embedTitle;
329
- frame.loading = 'lazy';
330
- frame.allowFullscreen = true;
331
- Object.assign(frame.style, {
332
- width: '100%',
333
- aspectRatio: embedAspectRatio,
334
- minHeight: '200px',
335
- border: '0',
336
- });
337
- container.replaceChildren(frame);
338
- } else {
339
- if (container.querySelector('button')) return;
340
- const button = document.createElement('button');
341
- button.type = 'button';
342
- button.textContent = 'Open privacy settings to view this content';
343
- button.onclick = openPreferences;
344
- container.replaceChildren(button);
345
- }
346
- };
347
-
348
- render();
349
- const unsubscribe = kernel.subscribe(render);
350
- return () => {
351
- unsubscribe();
352
- container.replaceChildren();
353
- };
358
+ const render = () => {
359
+ if (kernel.getSnapshot().effectivePermissions[embedCategory]) {
360
+ if (container.querySelector('iframe')) return;
361
+ const frame = document.createElement('iframe');
362
+ frame.src = embedURL;
363
+ frame.title = embedTitle;
364
+ frame.loading = 'lazy';
365
+ frame.allowFullscreen = true;
366
+ Object.assign(frame.style, {
367
+ width: '100%',
368
+ aspectRatio: embedAspectRatio,
369
+ minHeight: '200px',
370
+ border: '0',
371
+ });
372
+ container.replaceChildren(frame);
373
+ } else {
374
+ if (container.querySelector('button')) return;
375
+ const button = document.createElement('button');
376
+ button.type = 'button';
377
+ button.textContent = 'Open privacy settings to view this content';
378
+ button.onclick = openPreferences;
379
+ container.replaceChildren(button);
380
+ }
381
+ };
382
+
383
+ render();
384
+ const unsubscribe = kernel.subscribe(render);
385
+ return () => {
386
+ unsubscribe();
387
+ container.replaceChildren();
388
+ };
354
389
  }
355
390
  ```
356
391
 
@@ -381,8 +416,8 @@ Set playback options in the iframe URL. For example, append `start=36` to begin
381
416
  and reserve at least a 200-by-200-pixel player area.
382
417
 
383
418
  React and Svelte `ConsentGate` components provide a blocked-state placeholder and
384
- consent action. Their `placeholder` APIs differ: React takes a node, Svelte
385
- takes a snippet. Keep a clear way to reopen preferences when customizing them.
419
+ consent action. Their `placeholder` APIs differ. React takes a node, and
420
+ Svelte takes a snippet. Keep a clear way to reopen preferences when customizing them.
386
421
  The Vue and browser examples show that action explicitly. `loading="lazy"` delays an
387
422
  allowed iframe for performance; it does not implement consent gating.
388
423
 
@@ -391,7 +426,7 @@ allowed iframe for performance; it does not implement consent gating.
391
426
  Add `data-vendor="youtube"` next to `data-category` and declare a `youtube`
392
427
  vendor in the runtime's `vendors` option or the backend manifest to let visitors
393
428
  turn YouTube off while keeping the rest of the category on. See
394
- [granular consent](./granular-consent.md).
429
+ [vendor consent for your framework](./overview.md#vendor-switches-and-cookie-cleanup).
395
430
 
396
431
  Each example keeps the iframe absent until the category is allowed. After
397
432
  permission, the iframe can load. Revoking permission removes the iframe and its