@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.
- package/AGENTS.md +129 -59
- package/README.md +8 -29
- package/SKILL.md +33 -0
- package/dist/adobe-analytics.js +2 -0
- package/dist/ahrefs-analytics.js +2 -0
- package/dist/amplitude.js +2 -0
- package/dist/clearbit.js +2 -0
- package/dist/cloudflare-web-analytics.js +2 -0
- package/dist/cloudflare-zaraz.js +2 -0
- package/dist/crisp.js +2 -0
- package/dist/databuddy.js +2 -0
- package/dist/e2e-test-utils.js +2 -137
- package/dist/engine/compile.js +2 -89
- package/dist/engine/runtime.js +2 -448
- package/dist/events.js +2 -0
- package/dist/fathom-analytics.js +2 -0
- package/dist/front-chat.js +2 -0
- package/dist/google-tag-manager.js +2 -0
- package/dist/google-tag.js +2 -0
- package/dist/heap.js +2 -0
- package/dist/hightouch.js +2 -0
- package/dist/hotjar.js +2 -0
- package/dist/intercom.js +2 -0
- package/dist/klaviyo.js +2 -0
- package/dist/linkedin-insights.js +2 -0
- package/dist/logrocket.js +2 -0
- package/dist/matomo-analytics.js +2 -0
- package/dist/meta-pixel.js +2 -0
- package/dist/microsoft-clarity.js +2 -0
- package/dist/microsoft-uet.js +2 -0
- package/dist/mixpanel-analytics.js +2 -0
- package/dist/one-dollar-stats.js +2 -0
- package/dist/openai-pixel.js +2 -0
- package/dist/pinterest-tag.js +2 -0
- package/dist/pirsch.js +2 -0
- package/dist/plausible-analytics.js +2 -0
- package/dist/posthog.js +2 -0
- package/dist/promptwatch.js +2 -0
- package/dist/reddit-pixel.js +2 -0
- package/dist/registry.js +2 -392
- package/dist/resolve.js +2 -33
- package/dist/rudderstack.js +2 -0
- package/dist/rybbit-analytics.js +2 -0
- package/dist/segment.js +2 -0
- package/dist/snapchat-pixel.js +2 -0
- package/dist/tiktok-pixel.js +2 -0
- package/dist/types.js +2 -16
- package/dist/umami-analytics.js +2 -0
- package/dist/vendors/_shared/attributes.js +2 -14
- package/dist/vendors/_shared/google-consent.js +2 -27
- package/dist/vendors/_shared/install-builders.js +2 -21
- package/dist/vendors/_shared/required-id.js +2 -0
- package/dist/vendors/_shared/script-url.js +2 -28
- package/dist/vendors/ads-and-pixels/linkedin-insights.js +2 -48
- package/dist/vendors/ads-and-pixels/meta-pixel.js +2 -153
- package/dist/vendors/ads-and-pixels/microsoft-uet.js +2 -110
- package/dist/vendors/ads-and-pixels/openai-pixel.js +2 -88
- package/dist/vendors/ads-and-pixels/pinterest-tag.js +2 -0
- package/dist/vendors/ads-and-pixels/reddit-pixel.js +2 -107
- package/dist/vendors/ads-and-pixels/snapchat-pixel.js +2 -87
- package/dist/vendors/ads-and-pixels/tiktok-pixel.js +2 -89
- package/dist/vendors/ads-and-pixels/x-pixel.js +2 -48
- package/dist/vendors/analytics/adobe-analytics.js +2 -49
- package/dist/vendors/analytics/ahrefs-analytics.js +2 -27
- package/dist/vendors/analytics/amplitude.js +2 -134
- package/dist/vendors/analytics/clearbit.js +2 -28
- package/dist/vendors/analytics/cloudflare-web-analytics.js +2 -32
- package/dist/vendors/analytics/databuddy.js +2 -103
- package/dist/vendors/analytics/fathom-analytics.js +2 -35
- package/dist/vendors/analytics/google-tag.js +2 -66
- package/dist/vendors/analytics/heap.js +2 -134
- package/dist/vendors/analytics/hightouch.js +2 -109
- package/dist/vendors/analytics/hotjar.js +2 -44
- package/dist/vendors/analytics/logrocket.js +2 -58
- package/dist/vendors/analytics/matomo-analytics.js +2 -191
- package/dist/vendors/analytics/microsoft-clarity.js +2 -100
- package/dist/vendors/analytics/mixpanel-analytics.js +2 -93
- package/dist/vendors/analytics/one-dollar-stats.js +2 -0
- package/dist/vendors/analytics/pirsch.js +2 -67
- package/dist/vendors/analytics/plausible-analytics.js +2 -81
- package/dist/vendors/analytics/posthog.js +2 -200
- package/dist/vendors/analytics/promptwatch.js +2 -29
- package/dist/vendors/analytics/rudderstack.js +2 -183
- package/dist/vendors/analytics/rybbit-analytics.js +2 -63
- package/dist/vendors/analytics/segment.js +2 -56
- package/dist/vendors/analytics/umami-analytics.js +2 -39
- package/dist/vendors/analytics/vercel-analytics.js +2 -53
- package/dist/vendors/email-and-sms/klaviyo.js +2 -0
- package/dist/vendors/functional/crisp.js +2 -100
- package/dist/vendors/functional/front-chat.js +2 -0
- package/dist/vendors/functional/intercom.js +2 -45
- package/dist/vendors/tag-managers/cloudflare-zaraz.js +2 -98
- package/dist/vendors/tag-managers/google-tag-manager.js +2 -59
- package/dist/vercel-analytics.js +2 -0
- package/dist/x-pixel.js +2 -0
- package/dist-types/adobe-analytics.d.ts +2 -0
- package/dist-types/ahrefs-analytics.d.ts +2 -0
- package/dist-types/amplitude.d.ts +2 -0
- package/dist-types/clearbit.d.ts +2 -0
- package/dist-types/cloudflare-web-analytics.d.ts +2 -0
- package/dist-types/cloudflare-zaraz.d.ts +2 -0
- package/dist-types/crisp.d.ts +2 -0
- package/dist-types/databuddy.d.ts +2 -0
- package/dist-types/e2e-test-utils.d.ts +2 -0
- package/dist-types/engine/compile.d.ts +2 -3
- package/dist-types/engine/runtime.d.ts +2 -3
- package/dist-types/events.d.ts +2 -0
- package/dist-types/fathom-analytics.d.ts +2 -0
- package/dist-types/front-chat.d.ts +2 -0
- package/dist-types/google-tag-manager.d.ts +2 -0
- package/dist-types/google-tag.d.ts +2 -0
- package/dist-types/heap.d.ts +2 -0
- package/dist-types/hightouch.d.ts +2 -0
- package/dist-types/hotjar.d.ts +2 -0
- package/dist-types/intercom.d.ts +2 -0
- package/dist-types/klaviyo.d.ts +2 -0
- package/dist-types/linkedin-insights.d.ts +2 -0
- package/dist-types/logrocket.d.ts +2 -0
- package/dist-types/matomo-analytics.d.ts +2 -0
- package/dist-types/meta-pixel.d.ts +2 -0
- package/dist-types/microsoft-clarity.d.ts +2 -0
- package/dist-types/microsoft-uet.d.ts +2 -0
- package/dist-types/mixpanel-analytics.d.ts +2 -0
- package/dist-types/one-dollar-stats.d.ts +2 -0
- package/dist-types/openai-pixel.d.ts +2 -0
- package/dist-types/pinterest-tag.d.ts +2 -0
- package/dist-types/pirsch.d.ts +2 -0
- package/dist-types/plausible-analytics.d.ts +2 -0
- package/dist-types/posthog.d.ts +2 -0
- package/dist-types/promptwatch.d.ts +2 -0
- package/dist-types/reddit-pixel.d.ts +2 -0
- package/dist-types/registry.d.ts +2 -458
- package/dist-types/resolve.d.ts +2 -9
- package/dist-types/rudderstack.d.ts +2 -0
- package/dist-types/rybbit-analytics.d.ts +2 -0
- package/dist-types/segment.d.ts +2 -0
- package/dist-types/snapchat-pixel.d.ts +2 -0
- package/dist-types/tiktok-pixel.d.ts +2 -0
- package/dist-types/types.d.ts +2 -314
- package/dist-types/umami-analytics.d.ts +2 -0
- package/dist-types/vendors/_shared/attributes.d.ts +2 -35
- package/dist-types/vendors/_shared/google-consent.d.ts +2 -47
- package/dist-types/vendors/_shared/install-builders.d.ts +2 -30
- package/dist-types/vendors/_shared/required-id.d.ts +2 -0
- package/dist-types/vendors/_shared/script-url.d.ts +2 -75
- package/dist-types/vendors/ads-and-pixels/linkedin-insights.d.ts +2 -92
- package/dist-types/vendors/ads-and-pixels/meta-pixel.d.ts +2 -289
- package/dist-types/vendors/ads-and-pixels/microsoft-uet.d.ts +2 -105
- package/dist-types/vendors/ads-and-pixels/openai-pixel.d.ts +2 -211
- package/dist-types/vendors/ads-and-pixels/pinterest-tag.d.ts +2 -0
- package/dist-types/vendors/ads-and-pixels/reddit-pixel.d.ts +2 -210
- package/dist-types/vendors/ads-and-pixels/snapchat-pixel.d.ts +2 -171
- package/dist-types/vendors/ads-and-pixels/tiktok-pixel.d.ts +2 -106
- package/dist-types/vendors/ads-and-pixels/x-pixel.d.ts +2 -183
- package/dist-types/vendors/analytics/adobe-analytics.d.ts +2 -75
- package/dist-types/vendors/analytics/ahrefs-analytics.d.ts +2 -62
- package/dist-types/vendors/analytics/amplitude.d.ts +2 -234
- package/dist-types/vendors/analytics/clearbit.d.ts +2 -60
- package/dist-types/vendors/analytics/cloudflare-web-analytics.d.ts +2 -67
- package/dist-types/vendors/analytics/databuddy.d.ts +2 -147
- package/dist-types/vendors/analytics/fathom-analytics.d.ts +2 -90
- package/dist-types/vendors/analytics/google-tag.d.ts +2 -93
- package/dist-types/vendors/analytics/heap.d.ts +2 -316
- package/dist-types/vendors/analytics/hightouch.d.ts +2 -285
- package/dist-types/vendors/analytics/hotjar.d.ts +2 -73
- package/dist-types/vendors/analytics/logrocket.d.ts +2 -101
- package/dist-types/vendors/analytics/matomo-analytics.d.ts +2 -41
- package/dist-types/vendors/analytics/microsoft-clarity.d.ts +2 -97
- package/dist-types/vendors/analytics/mixpanel-analytics.d.ts +2 -113
- package/dist-types/vendors/analytics/one-dollar-stats.d.ts +2 -0
- package/dist-types/vendors/analytics/pirsch.d.ts +2 -96
- package/dist-types/vendors/analytics/plausible-analytics.d.ts +2 -122
- package/dist-types/vendors/analytics/posthog.d.ts +2 -175
- package/dist-types/vendors/analytics/promptwatch.d.ts +2 -36
- package/dist-types/vendors/analytics/rudderstack.d.ts +2 -330
- package/dist-types/vendors/analytics/rybbit-analytics.d.ts +2 -82
- package/dist-types/vendors/analytics/segment.d.ts +2 -158
- package/dist-types/vendors/analytics/umami-analytics.d.ts +2 -93
- package/dist-types/vendors/analytics/vercel-analytics.d.ts +2 -66
- package/dist-types/vendors/email-and-sms/klaviyo.d.ts +2 -0
- package/dist-types/vendors/functional/crisp.d.ts +2 -78
- package/dist-types/vendors/functional/front-chat.d.ts +2 -0
- package/dist-types/vendors/functional/intercom.d.ts +2 -135
- package/dist-types/vendors/tag-managers/cloudflare-zaraz.d.ts +2 -39
- package/dist-types/vendors/tag-managers/google-tag-manager.d.ts +2 -94
- package/dist-types/vercel-analytics.d.ts +2 -0
- package/dist-types/x-pixel.d.ts +2 -0
- package/docs/README.md +129 -59
- package/docs/assets/v3/bottom-bar.png +0 -0
- package/docs/assets/v3/brand-card.png +0 -0
- package/docs/assets/v3/brand-preferences.png +0 -0
- package/docs/assets/v3/choice-wall.png +0 -0
- package/docs/assets/v3/headless-bar-html.png +0 -0
- package/docs/assets/v3/headless-bar-mobile.png +0 -0
- package/docs/assets/v3/headless-bar.png +0 -0
- package/docs/assets/v3/slim-bar.png +0 -0
- package/docs/concepts/choose-your-setup.md +87 -0
- package/docs/concepts/consent-categories.md +84 -0
- package/docs/concepts/consent-state.md +357 -0
- package/docs/{guides → concepts}/data-fetching.md +31 -27
- package/docs/concepts/how-consent-works.md +123 -0
- package/docs/concepts/policies.md +71 -0
- package/docs/customization/class-names.md +202 -0
- package/docs/customization/dark-mode.md +157 -0
- package/docs/customization/motion.md +119 -0
- package/docs/customization/overview.md +67 -33
- package/docs/customization/recipes.md +839 -47
- package/docs/customization/slots.md +216 -35
- package/docs/customization/stylesheets.md +147 -0
- package/docs/customization/tailwind.md +842 -0
- package/docs/customization/tokens.md +166 -36
- package/docs/customization/translations.md +61 -3
- package/docs/frameworks/astro/embeds.md +160 -0
- package/docs/frameworks/astro/network-blocker.md +86 -0
- package/docs/frameworks/astro/scripts.md +146 -0
- package/docs/frameworks/html/embeds.md +142 -0
- package/docs/frameworks/html/network-blocker.md +105 -0
- package/docs/frameworks/html/scripts.md +164 -0
- package/docs/frameworks/javascript/scripts.md +119 -0
- package/docs/frameworks/next/embeds.md +90 -0
- package/docs/frameworks/next/network-blocker.md +153 -0
- package/docs/frameworks/next/scripts.md +196 -0
- package/docs/frameworks/nuxt/embeds.md +81 -0
- package/docs/frameworks/nuxt/network-blocker.md +97 -0
- package/docs/frameworks/nuxt/scripts.md +89 -0
- package/docs/frameworks/react/embeds.md +89 -0
- package/docs/frameworks/react/network-blocker.md +140 -0
- package/docs/frameworks/react/scripts.md +115 -0
- package/docs/frameworks/svelte/embeds.md +96 -0
- package/docs/frameworks/svelte/network-blocker.md +141 -0
- package/docs/frameworks/svelte/scripts.md +137 -0
- package/docs/frameworks/sveltekit/embeds.md +103 -0
- package/docs/frameworks/sveltekit/network-blocker.md +149 -0
- package/docs/frameworks/sveltekit/scripts.md +141 -0
- package/docs/frameworks/tanstack-start/embeds.md +96 -0
- package/docs/frameworks/tanstack-start/network-blocker.md +145 -0
- package/docs/frameworks/tanstack-start/scripts.md +103 -0
- package/docs/frameworks/vue/embeds.md +84 -0
- package/docs/frameworks/vue/network-blocker.md +99 -0
- package/docs/frameworks/vue/scripts.md +93 -0
- package/docs/guides/banner-experiments.md +654 -0
- package/docs/guides/troubleshooting.md +120 -47
- package/docs/guides/verify-consent.md +81 -49
- package/docs/integrations/adobe-analytics.md +168 -160
- package/docs/integrations/ahrefs-analytics.md +153 -155
- package/docs/integrations/amplitude.md +163 -157
- package/docs/integrations/building-integrations.md +136 -37
- package/docs/integrations/clearbit.md +155 -155
- package/docs/integrations/cloudflare-web-analytics.md +157 -157
- package/docs/integrations/cloudflare-zaraz.md +210 -262
- package/docs/integrations/crisp.md +165 -159
- package/docs/integrations/databuddy.md +158 -174
- package/docs/integrations/fathom-analytics.md +159 -157
- package/docs/integrations/front-chat.md +322 -0
- package/docs/integrations/google-maps.md +119 -84
- package/docs/integrations/google-tag-manager.md +179 -164
- package/docs/integrations/google-tag.md +164 -161
- package/docs/integrations/heap.md +164 -156
- package/docs/integrations/hightouch.md +162 -158
- package/docs/integrations/hotjar.md +160 -156
- package/docs/integrations/intercom.md +184 -154
- package/docs/integrations/klaviyo.md +486 -0
- package/docs/integrations/linkedin-insights.md +175 -151
- package/docs/integrations/logrocket.md +161 -157
- package/docs/integrations/matomo-analytics.md +189 -179
- package/docs/integrations/meta-pixel.md +189 -151
- package/docs/integrations/microsoft-clarity.md +164 -156
- package/docs/integrations/microsoft-uet.md +149 -155
- package/docs/integrations/mixpanel-analytics.md +156 -161
- package/docs/integrations/one-dollar-stats.md +306 -0
- package/docs/integrations/openai-pixel.md +205 -302
- package/docs/integrations/overview.md +143 -83
- package/docs/integrations/pinterest-tag.md +329 -0
- package/docs/integrations/pirsch.md +170 -160
- package/docs/integrations/plausible-analytics.md +173 -159
- package/docs/integrations/posthog.md +227 -242
- package/docs/integrations/promptwatch.md +155 -155
- package/docs/integrations/reddit-pixel.md +186 -158
- package/docs/integrations/rudderstack.md +202 -187
- package/docs/integrations/rybbit-analytics.md +172 -161
- package/docs/integrations/segment.md +183 -155
- package/docs/integrations/snapchat-pixel.md +187 -157
- package/docs/integrations/tiktok-pixel.md +172 -151
- package/docs/integrations/umami-analytics.md +164 -159
- package/docs/integrations/vercel-analytics.md +167 -158
- package/docs/integrations/x-pixel.md +177 -151
- package/docs/integrations/youtube.md +122 -87
- package/docs/upgrade-v3.md +490 -354
- package/package.json +12 -236
- package/dist-types/__tests__/helpers.d.ts +0 -141
- package/docs/assets/v3/brand-bar.png +0 -0
- package/docs/assets/v3/mobile-card.png +0 -0
- package/docs/assets/v3/preferences.png +0 -0
- package/docs/frameworks/javascript/script-loader.md +0 -94
- package/docs/frameworks/next/script-loader.md +0 -210
- package/docs/frameworks/react/script-loader.md +0 -63
- package/docs/guides/consent-state.md +0 -60
- package/docs/guides/deployment-modes.md +0 -75
- package/docs/integrations/clear-on-revocation.md +0 -167
- package/docs/integrations/granular-consent.md +0 -208
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Scripts
|
|
3
|
+
description: Load vendor scripts, iframes and network requests in a Svelte app
|
|
4
|
+
only after the visitor allows their consent category, and stop them when
|
|
5
|
+
consent is withdrawn.
|
|
6
|
+
group: frameworks
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Register scripts on the provider
|
|
10
|
+
|
|
11
|
+
Pass the array from `src/scripts.ts` to `ConsentManagerProvider` as the
|
|
12
|
+
`scripts` prop. The [quickstart](https://c15t.com/docs/frameworks/svelte/quickstart) sets up
|
|
13
|
+
this file:
|
|
14
|
+
|
|
15
|
+
```ts title="src/scripts.ts"
|
|
16
|
+
import { posthog } from '@c15t/integrations/posthog';
|
|
17
|
+
import { xPixel } from '@c15t/integrations/x-pixel';
|
|
18
|
+
|
|
19
|
+
export const scripts = [
|
|
20
|
+
posthog({
|
|
21
|
+
id: 'phc_your_project_key',
|
|
22
|
+
initOptions: { cookieless_mode: 'never' },
|
|
23
|
+
loadMode: 'after-consent',
|
|
24
|
+
}),
|
|
25
|
+
xPixel({ pixelId: 'your-pixel-id' }),
|
|
26
|
+
];
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Replace the placeholder PostHog project key and X Pixel ID with your own.
|
|
30
|
+
|
|
31
|
+
## How registered scripts load
|
|
32
|
+
|
|
33
|
+
The provider's `scripts` prop takes an array of script configurations. Each
|
|
34
|
+
has a category. The loader adds a script to the page when its category becomes
|
|
35
|
+
allowed and removes it when the category is withdrawn. Nothing optional loads
|
|
36
|
+
while the policy is still resolving, or when it fails.
|
|
37
|
+
|
|
38
|
+
Helpers in `@c15t/integrations`, such as `posthog()` from `@c15t/integrations/posthog`,
|
|
39
|
+
return a configuration with the right category and the vendor's own consent
|
|
40
|
+
calls. [Integrations](../../integrations/overview.md) lists every helper. For an
|
|
41
|
+
SDK without a helper, write a configuration with an `id`, `category` and `src`
|
|
42
|
+
as shown in [building integrations](../../integrations/building-integrations.md).
|
|
43
|
+
|
|
44
|
+
Remove the vendor's original `<script>` tag, `app.html` snippet or SDK import
|
|
45
|
+
before you register it. A banner does not block code that loads some other
|
|
46
|
+
way, and a vendor loaded twice sends events twice.
|
|
47
|
+
|
|
48
|
+
The `scripts` array is read when the provider is created. Build it once, at
|
|
49
|
+
the top level of the component, not inside an effect.
|
|
50
|
+
|
|
51
|
+
## Script options
|
|
52
|
+
|
|
53
|
+
Helpers set these for you. For a script without a helper, write the object
|
|
54
|
+
yourself:
|
|
55
|
+
|
|
56
|
+
| Option | Default | Behavior |
|
|
57
|
+
| ------------------------------------------------------------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------- |
|
|
58
|
+
| `id` | required | A unique name. The loader uses it to add and remove the script once. |
|
|
59
|
+
| `category` | required | The category or condition, such as `'measurement'` or `{ and: ['measurement', 'marketing'] }`, that must be allowed. |
|
|
60
|
+
| `src` or `textContent` | none | The script's URL, or inline code. |
|
|
61
|
+
| `callbackOnly` | `false` | Adds no `<script>` element and only runs the callbacks. Use it to switch an SDK you load yourself on and off. |
|
|
62
|
+
| `alwaysLoad` | `false` | Loads at once, whatever the consent state. Only for scripts that apply consent themselves, such as a tag manager with Consent Mode. |
|
|
63
|
+
| `persistAfterConsentRevoked` | `false` | Keeps the element after withdrawal instead of removing it. |
|
|
64
|
+
| `target` | `'head'` | Where the element goes: `'head'` or `'body'`. |
|
|
65
|
+
| `async`, `defer`, `fetchPriority`, `attributes`, `nonce` | none | Set on the `<script>` element. |
|
|
66
|
+
| `anonymizeId` | `true` | Gives the element a random `id`, so ad blockers do not match it by name. |
|
|
67
|
+
| `vendor` | none | Also waits for this vendor to be allowed, for vendor-level consent outside IAB. |
|
|
68
|
+
| `onBeforeLoad`, `onLoad`, `onError`, `onConsentChange`, `onDispose` | none | Lifecycle callbacks. See [callbacks](https://c15t.com/docs/frameworks/svelte/callbacks#script-callbacks). |
|
|
69
|
+
|
|
70
|
+
## Gate embeds
|
|
71
|
+
|
|
72
|
+
Iframes are not scripts. Wrap them in `ConsentGate`, which keeps the iframe
|
|
73
|
+
out of the DOM until its category is allowed:
|
|
74
|
+
|
|
75
|
+
```svelte title="src/YouTubeEmbed.svelte"
|
|
76
|
+
<script lang="ts">
|
|
77
|
+
import { ConsentDialogLink, ConsentGate } from '@c15t/svelte';
|
|
78
|
+
</script>
|
|
79
|
+
|
|
80
|
+
<!-- The iframe mounts only while measurement is allowed. -->
|
|
81
|
+
<ConsentGate category="measurement">
|
|
82
|
+
{#snippet placeholder()}<div class="placeholder">
|
|
83
|
+
<p>Allow measurement to load this YouTube video.</p>
|
|
84
|
+
<ConsentDialogLink>Choose video permissions</ConsentDialogLink>
|
|
85
|
+
</div>{/snippet}
|
|
86
|
+
<iframe
|
|
87
|
+
title="YouTube video"
|
|
88
|
+
src="https://www.youtube-nocookie.com/embed/czTksCF6X8Y?playsinline=1"
|
|
89
|
+
allow="encrypted-media; picture-in-picture"
|
|
90
|
+
allowfullscreen
|
|
91
|
+
></iframe>
|
|
92
|
+
</ConsentGate>
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
For iframes from a CMS or Markdown, which you cannot wrap, use the iframe
|
|
96
|
+
blocker's `data-category` and `data-src` attributes. [Embeds](./embeds.md) covers
|
|
97
|
+
both.
|
|
98
|
+
|
|
99
|
+
## Block requests from code already on the page
|
|
100
|
+
|
|
101
|
+
The provider's `networkBlocker` option holds `fetch` and `XMLHttpRequest`
|
|
102
|
+
calls to the domains you list until their category is allowed. It is a
|
|
103
|
+
backstop for code you cannot move into `scripts`. [Network blocker](./network-blocker.md)
|
|
104
|
+
covers the rules and what it cannot stop.
|
|
105
|
+
|
|
106
|
+
## What happens when consent is withdrawn
|
|
107
|
+
|
|
108
|
+
Removing a script tag cannot stop code that already ran. So when a save
|
|
109
|
+
withdraws a category that was granted, the provider reloads the page after the
|
|
110
|
+
save request, and the new page starts with only the permitted code. Set
|
|
111
|
+
`reloadOnConsentRevoked: false` on the provider if you handle withdrawal
|
|
112
|
+
yourself, for example through a vendor's own opt-out call in
|
|
113
|
+
`onConsentChange`.
|
|
114
|
+
|
|
115
|
+
`ConsentGate` content unmounts without a reload. To delete first-party cookies
|
|
116
|
+
a vendor set, configure `clearOnRevocation`; see
|
|
117
|
+
[clearing data on revocation](https://c15t.com/docs/frameworks/svelte/clear-on-revocation).
|
|
118
|
+
|
|
119
|
+
## Let visitors turn off one vendor
|
|
120
|
+
|
|
121
|
+
A visitor can allow marketing and still switch off one vendor in it. Pass
|
|
122
|
+
`vendors` to the provider; helpers from `@c15t/integrations` already carry their
|
|
123
|
+
vendor slug. See [vendor consent](https://c15t.com/docs/frameworks/svelte/vendor-consent).
|
|
124
|
+
|
|
125
|
+
## Verify vendor loading
|
|
126
|
+
|
|
127
|
+
Open DevTools, clear site data for your origin and reload:
|
|
128
|
+
|
|
129
|
+
1. With the banner showing, the Network panel has no requests to your vendors,
|
|
130
|
+
and gated iframes are absent from the Elements panel.
|
|
131
|
+
2. Allow one category in preferences. Only that category's vendors load, and
|
|
132
|
+
its iframes appear.
|
|
133
|
+
3. Reject, reload, and confirm the vendor requests stay absent.
|
|
134
|
+
4. Withdraw a category you allowed. The page reloads and its vendors no longer
|
|
135
|
+
load.
|
|
136
|
+
|
|
137
|
+
[Verify consent](../../guides/verify-consent.md) covers automated checks.
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Embeds
|
|
3
|
+
description: Keep YouTube videos, maps and other iframes out of SvelteKit server
|
|
4
|
+
HTML and the browser until their consent category is allowed.
|
|
5
|
+
group: frameworks
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Pick a way to gate the embed
|
|
9
|
+
|
|
10
|
+
A YouTube video, a map or a social post in an iframe contacts its vendor as
|
|
11
|
+
soon as it loads. c15t offers two ways to keep it from loading before
|
|
12
|
+
consent:
|
|
13
|
+
|
|
14
|
+
| Your markup | Use | Placeholder |
|
|
15
|
+
| -------------------------------------------------------- | ------------------------------------------------------ | ----------------------------- |
|
|
16
|
+
| A Svelte component you write | `ConsentGate` around the iframe | Built in, or your own snippet |
|
|
17
|
+
| 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 |
|
|
18
|
+
|
|
19
|
+
Both use the visitor's effective permission for one category, and both
|
|
20
|
+
remove the embed again when the visitor withdraws that category.
|
|
21
|
+
|
|
22
|
+
## Wrap the iframe in ConsentGate
|
|
23
|
+
|
|
24
|
+
```svelte title="src/YouTubeEmbed.svelte"
|
|
25
|
+
<script lang="ts">
|
|
26
|
+
import { ConsentDialogLink, ConsentGate } from '@c15t/svelte';
|
|
27
|
+
</script>
|
|
28
|
+
|
|
29
|
+
<!-- The iframe mounts only while measurement is allowed. -->
|
|
30
|
+
<ConsentGate category="measurement">
|
|
31
|
+
{#snippet placeholder()}<div class="placeholder">
|
|
32
|
+
<p>Allow measurement to load this YouTube video.</p>
|
|
33
|
+
<ConsentDialogLink>Choose video permissions</ConsentDialogLink>
|
|
34
|
+
</div>{/snippet}
|
|
35
|
+
<iframe
|
|
36
|
+
title="YouTube video"
|
|
37
|
+
src="https://www.youtube-nocookie.com/embed/czTksCF6X8Y?playsinline=1"
|
|
38
|
+
allow="encrypted-media; picture-in-picture"
|
|
39
|
+
allowfullscreen
|
|
40
|
+
></iframe>
|
|
41
|
+
</ConsentGate>
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
The iframe is absent from the DOM until measurement is allowed, so the
|
|
45
|
+
browser never requests it. The placeholder snippet replaces the built-in one
|
|
46
|
+
and includes a `ConsentDialogLink`, so a visitor can allow the category from
|
|
47
|
+
the embed's own spot. Keep `ConsentDialog` mounted for that link.
|
|
48
|
+
[ConsentGate](https://c15t.com/docs/frameworks/sveltekit/components/consent-gate) lists its props.
|
|
49
|
+
|
|
50
|
+
Pick the category the vendor's embed needs. YouTube and maps usually fit
|
|
51
|
+
`measurement` or `marketing`; check what each vendor sets.
|
|
52
|
+
|
|
53
|
+
## Gate iframes you do not render
|
|
54
|
+
|
|
55
|
+
The provider's iframe blocker is on by default. It watches the page for
|
|
56
|
+
iframes with a `data-category` attribute:
|
|
57
|
+
|
|
58
|
+
```html
|
|
59
|
+
<iframe
|
|
60
|
+
title="Store locations"
|
|
61
|
+
data-category="functionality"
|
|
62
|
+
data-src="https://www.google.com/maps/embed?pb=..."
|
|
63
|
+
></iframe>
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
* While the category is denied, the iframe has no `src`, so it loads nothing.
|
|
67
|
+
* When the category is allowed, the blocker copies `data-src` to `src`.
|
|
68
|
+
Only `http` and `https` URLs are used.
|
|
69
|
+
* When the category is withdrawn, the blocker removes `src` again.
|
|
70
|
+
|
|
71
|
+
Iframes without `data-category` or `data-vendor` are never touched. Put
|
|
72
|
+
`data-src` in the HTML instead of `src`; an iframe that arrives with `src`
|
|
73
|
+
already starts loading before the blocker sees it. Add `data-vendor` with a
|
|
74
|
+
vendor slug to also keep the iframe empty while the visitor has that vendor
|
|
75
|
+
turned off.
|
|
76
|
+
|
|
77
|
+
The blocker has no placeholder. Style the empty iframe, or place a note and a
|
|
78
|
+
`ConsentDialogLink` next to it.
|
|
79
|
+
|
|
80
|
+
Pass `iframeBlocker={false}` on the provider to turn the blocker off.
|
|
81
|
+
|
|
82
|
+
## Vendor guides
|
|
83
|
+
|
|
84
|
+
The [YouTube](../../integrations/youtube.md) and
|
|
85
|
+
[Google Maps](../../integrations/google-maps.md) guides have ready embed
|
|
86
|
+
configurations. [Integrations](../../integrations/overview.md) lists the rest.
|
|
87
|
+
|
|
88
|
+
## Verify the embeds
|
|
89
|
+
|
|
90
|
+
Clear site data and reload with DevTools open:
|
|
91
|
+
|
|
92
|
+
1. The Network panel has no request to the embed's host, and the Elements
|
|
93
|
+
panel shows the placeholder or an iframe without `src`.
|
|
94
|
+
2. Allow the category in preferences. The embed loads without a page reload.
|
|
95
|
+
3. Withdraw the category. The embed disappears, or its iframe loses `src`.
|
|
96
|
+
|
|
97
|
+
## Embeds in server HTML
|
|
98
|
+
|
|
99
|
+
`ConsentGate` renders an empty wrapper on the server, so a gated embed is
|
|
100
|
+
never in the server HTML, whatever the visitor chose. It appears in the
|
|
101
|
+
browser after hydration. An iframe with `data-category` and `data-src` is in
|
|
102
|
+
the server HTML without `src`, so it loads nothing until the iframe blocker
|
|
103
|
+
starts in the browser and finds its category allowed.
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Network blocker
|
|
3
|
+
description: Hold browser fetch and XMLHttpRequest calls to tracking domains in
|
|
4
|
+
a SvelteKit app until their consent category is allowed, with the
|
|
5
|
+
networkBlocker option.
|
|
6
|
+
group: frameworks
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## When to use the network blocker
|
|
10
|
+
|
|
11
|
+
The network blocker stops `fetch` and `XMLHttpRequest` calls to domains you
|
|
12
|
+
list until their consent category is allowed. Use it as a backstop for
|
|
13
|
+
tracking calls from code that is already on the page, such as your own
|
|
14
|
+
analytics wrapper or an SDK you import. It is off until you configure it.
|
|
15
|
+
|
|
16
|
+
Load vendor SDKs through the provider's `scripts` prop first; see
|
|
17
|
+
[scripts](./scripts.md). A script that never loads sends nothing, which is
|
|
18
|
+
stronger than blocking its requests one by one.
|
|
19
|
+
|
|
20
|
+
## Configure the rules
|
|
21
|
+
|
|
22
|
+
Write the configuration in its own module:
|
|
23
|
+
|
|
24
|
+
```ts title="src/lib/network-blocker.ts"
|
|
25
|
+
import type { UseNetworkBlockerOptions } from '@c15t/svelte';
|
|
26
|
+
|
|
27
|
+
// Pass as `networkBlocker={networkBlocker}` on ConsentManagerProvider.
|
|
28
|
+
export const networkBlocker: UseNetworkBlockerOptions = {
|
|
29
|
+
onRequestBlocked: ({ method, url }) => {
|
|
30
|
+
console.info('Blocked until consent', method, url);
|
|
31
|
+
},
|
|
32
|
+
rules: [
|
|
33
|
+
{
|
|
34
|
+
category: 'measurement',
|
|
35
|
+
domain: 'google-analytics.com',
|
|
36
|
+
id: 'google-analytics',
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
category: 'marketing',
|
|
40
|
+
domain: 'connect.facebook.net',
|
|
41
|
+
id: 'meta-pixel',
|
|
42
|
+
pathIncludes: '/signals',
|
|
43
|
+
},
|
|
44
|
+
],
|
|
45
|
+
};
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Pass it to the provider:
|
|
49
|
+
|
|
50
|
+
```svelte
|
|
51
|
+
<ConsentManagerProvider {mode} {scripts} {networkBlocker}>
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
The provider reads `networkBlocker` once, when it is created. Remount the
|
|
55
|
+
provider to change the rules.
|
|
56
|
+
|
|
57
|
+
## Match requests with rules
|
|
58
|
+
|
|
59
|
+
Each rule names a `domain` and the consent `category` a request needs. The
|
|
60
|
+
domain also matches its subdomains: `google-analytics.com` covers
|
|
61
|
+
`www.google-analytics.com`. `pathIncludes` narrows the rule to paths that
|
|
62
|
+
contain a substring, and `methods` narrows it to HTTP methods. A request is
|
|
63
|
+
blocked when a matching rule's condition is not met by the visitor's
|
|
64
|
+
effective permissions.
|
|
65
|
+
|
|
66
|
+
`category` takes the same conditions as scripts:
|
|
67
|
+
|
|
68
|
+
```ts
|
|
69
|
+
{ category: 'measurement' }
|
|
70
|
+
{ category: { and: ['measurement', 'marketing'] } }
|
|
71
|
+
{ category: { or: ['measurement', 'marketing'] } }
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Add `vendor` to also block the request while the visitor has turned that
|
|
75
|
+
vendor off. IAB TCF rules use `vendorId` and the `iab*` purpose fields
|
|
76
|
+
instead.
|
|
77
|
+
|
|
78
|
+
| Option | Default | Purpose |
|
|
79
|
+
| -------------------- | -------- | ------------------------------------------------------------ |
|
|
80
|
+
| `rules` | required | Rules described above |
|
|
81
|
+
| `enabled` | `true` | Set `false` to keep the rules but stop blocking |
|
|
82
|
+
| `logBlockedRequests` | `true` | Log each blocked request with `console.warn` |
|
|
83
|
+
| `onRequestBlocked` | none | Called with `{ method, url, rule }` for each blocked request |
|
|
84
|
+
|
|
85
|
+
## What a blocked request looks like
|
|
86
|
+
|
|
87
|
+
A blocked `fetch` resolves to a response with status `451` and the status
|
|
88
|
+
text `Request blocked by consent`, and nothing is sent. A blocked
|
|
89
|
+
`XMLHttpRequest` is aborted and fires an `error` event. Requests that match no
|
|
90
|
+
rule are not delayed. With `logBlockedRequests` on, the default, each blocked
|
|
91
|
+
request is logged with `console.warn`; `onRequestBlocked` receives
|
|
92
|
+
`{ method, url, rule }`.
|
|
93
|
+
|
|
94
|
+
## When blocking starts
|
|
95
|
+
|
|
96
|
+
The provider starts holding matching requests when it is created in the
|
|
97
|
+
browser, before its children run their own code. The blocker module loads
|
|
98
|
+
when the provider mounts and then decides each held request:
|
|
99
|
+
|
|
100
|
+
* While the policy is still loading, a matching request waits instead of
|
|
101
|
+
failing. When the policy arrives, with the visitor's stored choice applied,
|
|
102
|
+
the request is sent if its category is allowed and blocked otherwise.
|
|
103
|
+
* If the policy fails to load, optional categories stay denied and the
|
|
104
|
+
waiting requests are blocked.
|
|
105
|
+
* A synchronous XHR cannot wait. Before the blocker module loads, a matching
|
|
106
|
+
one throws a `NetworkError` from `send()`.
|
|
107
|
+
|
|
108
|
+
When the visitor allows a category later, new requests to its domains go
|
|
109
|
+
through. Requests blocked earlier are not replayed.
|
|
110
|
+
|
|
111
|
+
## What it cannot stop
|
|
112
|
+
|
|
113
|
+
The blocker sees only `fetch` and `XMLHttpRequest` calls made after the
|
|
114
|
+
provider is created in the browser. It cannot stop:
|
|
115
|
+
|
|
116
|
+
* Scripts that run before the provider, such as tags in `index.html` or
|
|
117
|
+
`app.html` and code at the top level of modules that load first.
|
|
118
|
+
* Code that kept its own reference to `fetch` or `XMLHttpRequest` from before
|
|
119
|
+
the provider was created.
|
|
120
|
+
* `navigator.sendBeacon`, `WebSocket`, `EventSource`, and requests made by
|
|
121
|
+
`<img>`, `<script>` and `<iframe>` elements, web workers and service
|
|
122
|
+
workers.
|
|
123
|
+
* Requests your server makes, such as from a SvelteKit `load` or endpoint.
|
|
124
|
+
|
|
125
|
+
Keep tracking calls out of that window. Send them from event handlers or
|
|
126
|
+
effects, not at a module's top level, and check
|
|
127
|
+
`getConsentManager().has('measurement')` before you call a vendor from your
|
|
128
|
+
own code.
|
|
129
|
+
|
|
130
|
+
## Verify the blocker
|
|
131
|
+
|
|
132
|
+
Open DevTools, clear site data for your origin and reload:
|
|
133
|
+
|
|
134
|
+
1. Before you choose, requests matching a rule do not reach the network, and
|
|
135
|
+
the console logs each blocked request.
|
|
136
|
+
2. Allow the rule's category. New matching requests appear in the Network
|
|
137
|
+
panel.
|
|
138
|
+
3. Reject, reload and confirm they stay blocked.
|
|
139
|
+
|
|
140
|
+
`fetch('https://www.google-analytics.com/g/collect')` in the console returns a
|
|
141
|
+
response with status `451` while measurement is denied.
|
|
142
|
+
|
|
143
|
+
## Server requests are not blocked
|
|
144
|
+
|
|
145
|
+
The network blocker runs in the browser. A `fetch` in a SvelteKit `load`,
|
|
146
|
+
`+server.ts` or `hooks.server.ts` is never blocked, even when the visitor has
|
|
147
|
+
denied the category. If server code sends data to a vendor, check the
|
|
148
|
+
visitor's stored choice yourself before it does; `event.locals.c15t.config`
|
|
149
|
+
from [`c15tHandle`](https://c15t.com/docs/frameworks/sveltekit/server-api#c15thandle) holds it.
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Scripts
|
|
3
|
+
description: Load vendor scripts, iframes and network requests in a SvelteKit
|
|
4
|
+
app only after the visitor allows their consent category, and stop them when
|
|
5
|
+
consent is withdrawn.
|
|
6
|
+
group: frameworks
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Register scripts in the root layout
|
|
10
|
+
|
|
11
|
+
Pass the array from `src/lib/example-scripts.ts` to `ConsentManagerProvider`
|
|
12
|
+
as the `scripts` prop in `src/routes/+layout.svelte`. The
|
|
13
|
+
[quickstart](https://c15t.com/docs/frameworks/sveltekit/quickstart) sets up both files:
|
|
14
|
+
|
|
15
|
+
```ts title="src/lib/example-scripts.ts"
|
|
16
|
+
import { posthog } from '@c15t/integrations/posthog';
|
|
17
|
+
import { xPixel } from '@c15t/integrations/x-pixel';
|
|
18
|
+
|
|
19
|
+
export const scripts = [
|
|
20
|
+
posthog({
|
|
21
|
+
id: 'phc_your_project_key',
|
|
22
|
+
initOptions: { cookieless_mode: 'never' },
|
|
23
|
+
loadMode: 'after-consent',
|
|
24
|
+
region: 'eu',
|
|
25
|
+
}),
|
|
26
|
+
xPixel({ pixelId: 'your-pixel-id' }),
|
|
27
|
+
];
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Create the scripts in the layout component, not in `+layout.server.ts`. Script
|
|
31
|
+
configurations hold functions, which a server load cannot send to the
|
|
32
|
+
browser. Scripts load only in the browser, after hydration, so they never
|
|
33
|
+
appear in server HTML.
|
|
34
|
+
|
|
35
|
+
## How registered scripts load
|
|
36
|
+
|
|
37
|
+
The provider's `scripts` prop takes an array of script configurations. Each
|
|
38
|
+
has a category. The loader adds a script to the page when its category becomes
|
|
39
|
+
allowed and removes it when the category is withdrawn. Nothing optional loads
|
|
40
|
+
while the policy is still resolving, or when it fails.
|
|
41
|
+
|
|
42
|
+
Helpers in `@c15t/integrations`, such as `posthog()` from `@c15t/integrations/posthog`,
|
|
43
|
+
return a configuration with the right category and the vendor's own consent
|
|
44
|
+
calls. [Integrations](../../integrations/overview.md) lists every helper. For an
|
|
45
|
+
SDK without a helper, write a configuration with an `id`, `category` and `src`
|
|
46
|
+
as shown in [building integrations](../../integrations/building-integrations.md).
|
|
47
|
+
|
|
48
|
+
Remove the vendor's original `<script>` tag, `app.html` snippet or SDK import
|
|
49
|
+
before you register it. A banner does not block code that loads some other
|
|
50
|
+
way, and a vendor loaded twice sends events twice.
|
|
51
|
+
|
|
52
|
+
The `scripts` array is read when the provider is created. Build it once, at
|
|
53
|
+
the top level of the component, not inside an effect.
|
|
54
|
+
|
|
55
|
+
## Script options
|
|
56
|
+
|
|
57
|
+
Helpers set these for you. For a script without a helper, write the object
|
|
58
|
+
yourself:
|
|
59
|
+
|
|
60
|
+
| Option | Default | Behavior |
|
|
61
|
+
| ------------------------------------------------------------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------- |
|
|
62
|
+
| `id` | required | A unique name. The loader uses it to add and remove the script once. |
|
|
63
|
+
| `category` | required | The category or condition, such as `'measurement'` or `{ and: ['measurement', 'marketing'] }`, that must be allowed. |
|
|
64
|
+
| `src` or `textContent` | none | The script's URL, or inline code. |
|
|
65
|
+
| `callbackOnly` | `false` | Adds no `<script>` element and only runs the callbacks. Use it to switch an SDK you load yourself on and off. |
|
|
66
|
+
| `alwaysLoad` | `false` | Loads at once, whatever the consent state. Only for scripts that apply consent themselves, such as a tag manager with Consent Mode. |
|
|
67
|
+
| `persistAfterConsentRevoked` | `false` | Keeps the element after withdrawal instead of removing it. |
|
|
68
|
+
| `target` | `'head'` | Where the element goes: `'head'` or `'body'`. |
|
|
69
|
+
| `async`, `defer`, `fetchPriority`, `attributes`, `nonce` | none | Set on the `<script>` element. |
|
|
70
|
+
| `anonymizeId` | `true` | Gives the element a random `id`, so ad blockers do not match it by name. |
|
|
71
|
+
| `vendor` | none | Also waits for this vendor to be allowed, for vendor-level consent outside IAB. |
|
|
72
|
+
| `onBeforeLoad`, `onLoad`, `onError`, `onConsentChange`, `onDispose` | none | Lifecycle callbacks. See [callbacks](https://c15t.com/docs/frameworks/sveltekit/callbacks#script-callbacks). |
|
|
73
|
+
|
|
74
|
+
## Gate embeds
|
|
75
|
+
|
|
76
|
+
Iframes are not scripts. Wrap them in `ConsentGate`, which keeps the iframe
|
|
77
|
+
out of the DOM until its category is allowed:
|
|
78
|
+
|
|
79
|
+
```svelte title="src/YouTubeEmbed.svelte"
|
|
80
|
+
<script lang="ts">
|
|
81
|
+
import { ConsentDialogLink, ConsentGate } from '@c15t/svelte';
|
|
82
|
+
</script>
|
|
83
|
+
|
|
84
|
+
<!-- The iframe mounts only while measurement is allowed. -->
|
|
85
|
+
<ConsentGate category="measurement">
|
|
86
|
+
{#snippet placeholder()}<div class="placeholder">
|
|
87
|
+
<p>Allow measurement to load this YouTube video.</p>
|
|
88
|
+
<ConsentDialogLink>Choose video permissions</ConsentDialogLink>
|
|
89
|
+
</div>{/snippet}
|
|
90
|
+
<iframe
|
|
91
|
+
title="YouTube video"
|
|
92
|
+
src="https://www.youtube-nocookie.com/embed/czTksCF6X8Y?playsinline=1"
|
|
93
|
+
allow="encrypted-media; picture-in-picture"
|
|
94
|
+
allowfullscreen
|
|
95
|
+
></iframe>
|
|
96
|
+
</ConsentGate>
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
For iframes from a CMS or Markdown, which you cannot wrap, use the iframe
|
|
100
|
+
blocker's `data-category` and `data-src` attributes. [Embeds](./embeds.md) covers
|
|
101
|
+
both.
|
|
102
|
+
|
|
103
|
+
## Block requests from code already on the page
|
|
104
|
+
|
|
105
|
+
The provider's `networkBlocker` option holds `fetch` and `XMLHttpRequest`
|
|
106
|
+
calls to the domains you list until their category is allowed. It is a
|
|
107
|
+
backstop for code you cannot move into `scripts`. [Network blocker](./network-blocker.md)
|
|
108
|
+
covers the rules and what it cannot stop.
|
|
109
|
+
|
|
110
|
+
## What happens when consent is withdrawn
|
|
111
|
+
|
|
112
|
+
Removing a script tag cannot stop code that already ran. So when a save
|
|
113
|
+
withdraws a category that was granted, the provider reloads the page after the
|
|
114
|
+
save request, and the new page starts with only the permitted code. Set
|
|
115
|
+
`reloadOnConsentRevoked: false` on the provider if you handle withdrawal
|
|
116
|
+
yourself, for example through a vendor's own opt-out call in
|
|
117
|
+
`onConsentChange`.
|
|
118
|
+
|
|
119
|
+
`ConsentGate` content unmounts without a reload. To delete first-party cookies
|
|
120
|
+
a vendor set, configure `clearOnRevocation`; see
|
|
121
|
+
[clearing data on revocation](https://c15t.com/docs/frameworks/sveltekit/clear-on-revocation).
|
|
122
|
+
|
|
123
|
+
## Let visitors turn off one vendor
|
|
124
|
+
|
|
125
|
+
A visitor can allow marketing and still switch off one vendor in it. Pass
|
|
126
|
+
`vendors` to the provider; helpers from `@c15t/integrations` already carry their
|
|
127
|
+
vendor slug. See [vendor consent](https://c15t.com/docs/frameworks/sveltekit/vendor-consent).
|
|
128
|
+
|
|
129
|
+
## Verify vendor loading
|
|
130
|
+
|
|
131
|
+
Open DevTools, clear site data for your origin and reload:
|
|
132
|
+
|
|
133
|
+
1. With the banner showing, the Network panel has no requests to your vendors,
|
|
134
|
+
and gated iframes are absent from the Elements panel.
|
|
135
|
+
2. Allow one category in preferences. Only that category's vendors load, and
|
|
136
|
+
its iframes appear.
|
|
137
|
+
3. Reject, reload, and confirm the vendor requests stay absent.
|
|
138
|
+
4. Withdraw a category you allowed. The page reloads and its vendors no longer
|
|
139
|
+
load.
|
|
140
|
+
|
|
141
|
+
[Verify consent](../../guides/verify-consent.md) covers automated checks.
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Embeds
|
|
3
|
+
description: Keep YouTube videos, maps and other iframes out of a TanStack Start
|
|
4
|
+
page until their consent category is allowed, with ConsentGate or the iframe
|
|
5
|
+
blocker in ConsentRoot.
|
|
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 HTML 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` in any route under the root route that
|
|
22
|
+
mounts `ConsentRoot`:
|
|
23
|
+
|
|
24
|
+
```tsx title="src/components/video-embed.tsx"
|
|
25
|
+
import { ConsentDialogLink, ConsentGate } from 'c15t/tanstack-start';
|
|
26
|
+
|
|
27
|
+
export const VideoEmbed = () => (
|
|
28
|
+
<ConsentGate
|
|
29
|
+
category="measurement"
|
|
30
|
+
placeholder={
|
|
31
|
+
<div>
|
|
32
|
+
<p>
|
|
33
|
+
Allow measurement to load this YouTube video. No video request is sent
|
|
34
|
+
before permission.
|
|
35
|
+
</p>
|
|
36
|
+
<ConsentDialogLink>Open privacy settings</ConsentDialogLink>
|
|
37
|
+
</div>
|
|
38
|
+
}
|
|
39
|
+
>
|
|
40
|
+
<iframe
|
|
41
|
+
src="https://www.youtube-nocookie.com/embed/czTksCF6X8Y"
|
|
42
|
+
title="YouTube video"
|
|
43
|
+
sandbox="allow-scripts allow-same-origin allow-presentation"
|
|
44
|
+
allowFullScreen
|
|
45
|
+
/>
|
|
46
|
+
</ConsentGate>
|
|
47
|
+
);
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
The server HTML contains the placeholder, never the iframe, unless the root
|
|
51
|
+
loader resolved measurement as allowed. The placeholder shows a message and a
|
|
52
|
+
[`ConsentDialogLink`](https://c15t.com/docs/frameworks/tanstack-start/components/consent-dialog-link)
|
|
53
|
+
until then. When the visitor withdraws measurement, React removes the iframe.
|
|
54
|
+
|
|
55
|
+
Pick the category that matches what the embed does. The example uses
|
|
56
|
+
measurement for YouTube because its player measures views. A map or chat
|
|
57
|
+
widget usually belongs under functionality or experience.
|
|
58
|
+
[ConsentGate](https://c15t.com/docs/frameworks/tanstack-start/components/consent-gate)
|
|
59
|
+
documents the built-in placeholder and every prop.
|
|
60
|
+
|
|
61
|
+
## Gate an iframe with the iframe blocker
|
|
62
|
+
|
|
63
|
+
`ConsentRoot` runs the iframe blocker in the browser by default. Give an
|
|
64
|
+
iframe `data-src` instead of `src`, and a `data-category`:
|
|
65
|
+
|
|
66
|
+
```html title="Markup from your CMS"
|
|
67
|
+
<iframe
|
|
68
|
+
data-src="https://www.google.com/maps/embed?pb=YOUR_EMBED_ID"
|
|
69
|
+
data-category="functionality"
|
|
70
|
+
title="Store map"
|
|
71
|
+
></iframe>
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
The server HTML has no `src`, so nothing loads before hydration. After
|
|
75
|
+
hydration, c15t sets `src` from `data-src` when the category is allowed and
|
|
76
|
+
removes `src` when the category is withdrawn. It watches the page, so iframes
|
|
77
|
+
added by client-side navigation are handled too. Add `data-vendor` with a
|
|
78
|
+
vendor ID to also block the iframe while the visitor has turned that vendor
|
|
79
|
+
off.
|
|
80
|
+
|
|
81
|
+
To turn the blocker off, set `iframeBlocker: false` in `ConsentRoot`'s
|
|
82
|
+
`options`.
|
|
83
|
+
|
|
84
|
+
## Verify the embeds
|
|
85
|
+
|
|
86
|
+
Open the production build in a private window with DevTools open, under a
|
|
87
|
+
policy that asks for consent.
|
|
88
|
+
|
|
89
|
+
1. View the page source. No iframe has a `src` pointing at
|
|
90
|
+
`youtube-nocookie.com` or `google.com/maps`, and the Network panel shows no
|
|
91
|
+
request to them.
|
|
92
|
+
2. Allow the embed's category and save. The iframe loads.
|
|
93
|
+
3. Withdraw the category and save. The page reloads without the embed.
|
|
94
|
+
|
|
95
|
+
The [YouTube](../../integrations/youtube.md) and
|
|
96
|
+
[Google Maps](../../integrations/google-maps.md) guides cover sizing and titles.
|