@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.
- package/AGENTS.md +129 -63
- 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 -139
- package/dist/engine/compile.js +2 -89
- package/dist/engine/runtime.js +2 -448
- package/dist/events.js +2 -218
- 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 -422
- 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 -123
- 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 -78
- 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 -30
- 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 -65
- 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 -64
- 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 -73
- 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 -46
- 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 -485
- 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 -295
- 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 -95
- 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 -39
- 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 -164
- 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 -62
- 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 -96
- package/dist-types/vercel-analytics.d.ts +2 -0
- package/dist-types/x-pixel.d.ts +2 -0
- package/docs/README.md +129 -63
- 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/{guides → concepts}/consent-state.md +89 -105
- 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 -34
- package/docs/customization/recipes.md +839 -49
- 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 +163 -96
- package/docs/customization/translations.md +60 -3
- package/docs/frameworks/astro/embeds.md +160 -0
- package/docs/frameworks/astro/network-blocker.md +86 -0
- package/docs/frameworks/astro/scripts.md +155 -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 +137 -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 +144 -0
- package/docs/frameworks/sveltekit/embeds.md +103 -0
- package/docs/frameworks/sveltekit/network-blocker.md +159 -0
- package/docs/frameworks/sveltekit/scripts.md +172 -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 +167 -159
- package/docs/integrations/ahrefs-analytics.md +152 -154
- package/docs/integrations/amplitude.md +162 -156
- package/docs/integrations/building-integrations.md +136 -37
- package/docs/integrations/clearbit.md +154 -154
- package/docs/integrations/cloudflare-web-analytics.md +156 -156
- package/docs/integrations/cloudflare-zaraz.md +209 -261
- package/docs/integrations/crisp.md +164 -158
- package/docs/integrations/databuddy.md +157 -173
- package/docs/integrations/fathom-analytics.md +158 -156
- package/docs/integrations/front-chat.md +167 -167
- package/docs/integrations/google-maps.md +118 -83
- package/docs/integrations/google-tag-manager.md +178 -163
- package/docs/integrations/google-tag.md +163 -160
- package/docs/integrations/heap.md +163 -155
- package/docs/integrations/hightouch.md +161 -157
- package/docs/integrations/hotjar.md +159 -155
- package/docs/integrations/intercom.md +183 -153
- package/docs/integrations/klaviyo.md +486 -0
- package/docs/integrations/linkedin-insights.md +174 -150
- package/docs/integrations/logrocket.md +160 -156
- package/docs/integrations/matomo-analytics.md +188 -178
- package/docs/integrations/meta-pixel.md +188 -150
- package/docs/integrations/microsoft-clarity.md +163 -155
- package/docs/integrations/microsoft-uet.md +148 -154
- package/docs/integrations/mixpanel-analytics.md +155 -160
- package/docs/integrations/one-dollar-stats.md +166 -165
- package/docs/integrations/openai-pixel.md +204 -301
- package/docs/integrations/overview.md +137 -80
- package/docs/integrations/pinterest-tag.md +191 -183
- package/docs/integrations/pirsch.md +169 -159
- package/docs/integrations/plausible-analytics.md +172 -158
- package/docs/integrations/posthog.md +313 -240
- package/docs/integrations/promptwatch.md +154 -154
- package/docs/integrations/reddit-pixel.md +185 -157
- package/docs/integrations/rudderstack.md +201 -186
- package/docs/integrations/rybbit-analytics.md +171 -160
- package/docs/integrations/segment.md +182 -154
- package/docs/integrations/snapchat-pixel.md +186 -156
- package/docs/integrations/tiktok-pixel.md +171 -150
- package/docs/integrations/umami-analytics.md +163 -158
- package/docs/integrations/vercel-analytics.md +166 -157
- package/docs/integrations/x-pixel.md +176 -150
- package/docs/integrations/youtube.md +121 -86
- package/docs/upgrade-v3.md +496 -467
- package/package.json +10 -257
- 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 -100
- package/docs/frameworks/next/script-loader.md +0 -216
- package/docs/frameworks/react/script-loader.md +0 -69
- package/docs/guides/deployment-modes.md +0 -75
- package/docs/guides/shared-consent-controls.md +0 -158
- package/docs/integrations/clear-on-revocation.md +0 -167
- package/docs/integrations/granular-consent.md +0 -210
|
@@ -0,0 +1,172 @@
|
|
|
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
|
+
## Preload the script loader
|
|
36
|
+
|
|
37
|
+
The provider loads the script loader only on pages whose `scripts` array is
|
|
38
|
+
not empty or that have [network blocker](./network-blocker.md) rules. The loader
|
|
39
|
+
and the blocker share one separate chunk. Pages with neither never download
|
|
40
|
+
it. On a page with either, the browser would otherwise request the chunk after
|
|
41
|
+
the app's JavaScript has run, and a returning visitor's consented scripts and
|
|
42
|
+
held requests would wait for that extra request.
|
|
43
|
+
|
|
44
|
+
Add the `c15tPreload()` Vite plugin so `c15tHandle` can link the chunk from
|
|
45
|
+
the page's `<head>` with `<link rel="modulepreload">`:
|
|
46
|
+
|
|
47
|
+
```ts title="vite.config.ts (partial)"
|
|
48
|
+
import { c15tPreload } from '@c15t/svelte/vite';
|
|
49
|
+
|
|
50
|
+
export default defineConfig({ plugins: [sveltekit(), c15tPreload()] });
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
SvelteKit builds the server before the client, so the server cannot know the
|
|
54
|
+
chunk's file name. After the client build, the plugin writes the chunk URL
|
|
55
|
+
into the server output, before SvelteKit prerenders pages and before the
|
|
56
|
+
adapter copies the build. Then `c15tHandle` adds one link to every page whose
|
|
57
|
+
provider has scripts or blocker rules, prerendered pages included. The link
|
|
58
|
+
carries the provider's `nonce`, or else the nonce SvelteKit put on its own
|
|
59
|
+
scripts. It asks for low priority (`fetchpriority="low"`): the
|
|
60
|
+
runtime needs the chunk only after hydration, so the browser fetches your
|
|
61
|
+
app's own chunks first. It does nothing in `vite dev`.
|
|
62
|
+
|
|
63
|
+
Without the plugin, or without `c15tHandle` in `hooks.server.ts`, scripts
|
|
64
|
+
still load, one request later.
|
|
65
|
+
|
|
66
|
+
## How registered scripts load
|
|
67
|
+
|
|
68
|
+
The provider's `scripts` prop takes an array of script configurations. Each
|
|
69
|
+
has a category. The loader adds a script to the page when its category becomes
|
|
70
|
+
allowed and removes it when the category is withdrawn. Nothing optional loads
|
|
71
|
+
while the policy is still resolving, or when it fails.
|
|
72
|
+
|
|
73
|
+
Helpers in `@c15t/integrations`, such as `posthog()` from `@c15t/integrations/posthog`,
|
|
74
|
+
return a configuration with the right category and the vendor's own consent
|
|
75
|
+
calls. [Integrations](../../integrations/overview.md) lists every helper. For an
|
|
76
|
+
SDK without a helper, write a configuration with an `id`, `category` and `src`
|
|
77
|
+
as shown in [building integrations](../../integrations/building-integrations.md).
|
|
78
|
+
|
|
79
|
+
Remove the vendor's original `<script>` tag, `app.html` snippet or SDK import
|
|
80
|
+
before you register it. A banner does not block code that loads some other
|
|
81
|
+
way, and a vendor loaded twice sends events twice.
|
|
82
|
+
|
|
83
|
+
The `scripts` array is read when the provider is created. Build it once, at
|
|
84
|
+
the top level of the component, not inside an effect.
|
|
85
|
+
|
|
86
|
+
## Script options
|
|
87
|
+
|
|
88
|
+
Helpers set these for you. For a script without a helper, write the object
|
|
89
|
+
yourself:
|
|
90
|
+
|
|
91
|
+
| Option | Default | Behavior |
|
|
92
|
+
| ------------------------------------------------------------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------- |
|
|
93
|
+
| `id` | required | A unique name. The loader uses it to add and remove the script once. |
|
|
94
|
+
| `category` | required | The category or condition, such as `'measurement'` or `{ and: ['measurement', 'marketing'] }`, that must be allowed. |
|
|
95
|
+
| `src` or `textContent` | none | The script's URL, or inline code. |
|
|
96
|
+
| `callbackOnly` | `false` | Adds no `<script>` element and only runs the callbacks. Use it to switch an SDK you load yourself on and off. |
|
|
97
|
+
| `alwaysLoad` | `false` | Loads at once, whatever the consent state. Only for scripts that apply consent themselves, such as a tag manager with Consent Mode. |
|
|
98
|
+
| `persistAfterConsentRevoked` | `false` | Keeps the element after withdrawal instead of removing it. |
|
|
99
|
+
| `target` | `'head'` | Where the element goes: `'head'` or `'body'`. |
|
|
100
|
+
| `async`, `defer`, `fetchPriority`, `attributes`, `nonce` | none | Set on the `<script>` element. |
|
|
101
|
+
| `anonymizeId` | `true` | Gives the element a random `id`, so ad blockers do not match it by name. |
|
|
102
|
+
| `vendor` | none | Also waits for this vendor to be allowed, for vendor-level consent outside IAB. |
|
|
103
|
+
| `onBeforeLoad`, `onLoad`, `onError`, `onConsentChange`, `onDispose` | none | Lifecycle callbacks. See [callbacks](https://c15t.com/docs/frameworks/sveltekit/callbacks#script-callbacks). |
|
|
104
|
+
|
|
105
|
+
## Gate embeds
|
|
106
|
+
|
|
107
|
+
Iframes are not scripts. Wrap them in `ConsentGate`, which keeps the iframe
|
|
108
|
+
out of the DOM until its category is allowed:
|
|
109
|
+
|
|
110
|
+
```svelte title="src/YouTubeEmbed.svelte"
|
|
111
|
+
<script lang="ts">
|
|
112
|
+
import { ConsentDialogLink, ConsentGate } from '@c15t/svelte';
|
|
113
|
+
</script>
|
|
114
|
+
|
|
115
|
+
<!-- The iframe mounts only while measurement is allowed. -->
|
|
116
|
+
<ConsentGate category="measurement">
|
|
117
|
+
{#snippet placeholder()}<div class="placeholder">
|
|
118
|
+
<p>Allow measurement to load this YouTube video.</p>
|
|
119
|
+
<ConsentDialogLink>Choose video permissions</ConsentDialogLink>
|
|
120
|
+
</div>{/snippet}
|
|
121
|
+
<iframe
|
|
122
|
+
title="YouTube video"
|
|
123
|
+
src="https://www.youtube-nocookie.com/embed/czTksCF6X8Y?playsinline=1"
|
|
124
|
+
allow="encrypted-media; picture-in-picture"
|
|
125
|
+
allowfullscreen
|
|
126
|
+
></iframe>
|
|
127
|
+
</ConsentGate>
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
For iframes from a CMS or Markdown, which you cannot wrap, use the iframe
|
|
131
|
+
blocker's `data-category` and `data-src` attributes. [Embeds](./embeds.md) covers
|
|
132
|
+
both.
|
|
133
|
+
|
|
134
|
+
## Block requests from code already on the page
|
|
135
|
+
|
|
136
|
+
The provider's `networkBlocker` option holds `fetch` and `XMLHttpRequest`
|
|
137
|
+
calls to the domains you list until their category is allowed. It is a
|
|
138
|
+
backstop for code you cannot move into `scripts`. [Network blocker](./network-blocker.md)
|
|
139
|
+
covers the rules and what it cannot stop.
|
|
140
|
+
|
|
141
|
+
## What happens when consent is withdrawn
|
|
142
|
+
|
|
143
|
+
Removing a script tag cannot stop code that already ran. So when a save
|
|
144
|
+
withdraws a category that was granted, the provider reloads the page after the
|
|
145
|
+
save request, and the new page starts with only the permitted code. Set
|
|
146
|
+
`reloadOnConsentRevoked: false` on the provider if you handle withdrawal
|
|
147
|
+
yourself, for example through a vendor's own opt-out call in
|
|
148
|
+
`onConsentChange`.
|
|
149
|
+
|
|
150
|
+
`ConsentGate` content unmounts without a reload. To delete first-party cookies
|
|
151
|
+
a vendor set, configure `clearOnRevocation`; see
|
|
152
|
+
[clearing data on revocation](https://c15t.com/docs/frameworks/sveltekit/clear-on-revocation).
|
|
153
|
+
|
|
154
|
+
## Let visitors turn off one vendor
|
|
155
|
+
|
|
156
|
+
A visitor can allow marketing and still switch off one vendor in it. Pass
|
|
157
|
+
`vendors` to the provider; helpers from `@c15t/integrations` already carry their
|
|
158
|
+
vendor slug. See [vendor consent](https://c15t.com/docs/frameworks/sveltekit/vendor-consent).
|
|
159
|
+
|
|
160
|
+
## Verify vendor loading
|
|
161
|
+
|
|
162
|
+
Open DevTools, clear site data for your origin and reload:
|
|
163
|
+
|
|
164
|
+
1. With the banner showing, the Network panel has no requests to your vendors,
|
|
165
|
+
and gated iframes are absent from the Elements panel.
|
|
166
|
+
2. Allow one category in preferences. Only that category's vendors load, and
|
|
167
|
+
its iframes appear.
|
|
168
|
+
3. Reject, reload, and confirm the vendor requests stay absent.
|
|
169
|
+
4. Withdraw a category you allowed. The page reloads and its vendors no longer
|
|
170
|
+
load.
|
|
171
|
+
|
|
172
|
+
[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.
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Network blocker
|
|
3
|
+
description: Hold fetch and XMLHttpRequest calls in a TanStack Start app until
|
|
4
|
+
their consent category is allowed, with networkBlocker rules on ConsentRoot.
|
|
5
|
+
group: frameworks
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Add rules
|
|
9
|
+
|
|
10
|
+
Pass `networkBlocker` rules to `ConsentRoot` to stop `fetch` and
|
|
11
|
+
`XMLHttpRequest` calls to a domain until its category is allowed. Use it as a
|
|
12
|
+
backstop for beacons an SDK sends itself. Load vendor SDKs through
|
|
13
|
+
[`scripts`](./scripts.md) first, so they do not run
|
|
14
|
+
at all before consent.
|
|
15
|
+
|
|
16
|
+
```tsx title="src/routes/__root.tsx"
|
|
17
|
+
const networkBlocker = {
|
|
18
|
+
rules: [
|
|
19
|
+
{
|
|
20
|
+
id: 'google-analytics',
|
|
21
|
+
domain: 'google-analytics.com',
|
|
22
|
+
category: 'measurement',
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
id: 'meta-pixel',
|
|
26
|
+
domain: 'facebook.com',
|
|
27
|
+
pathIncludes: '/tr',
|
|
28
|
+
category: 'marketing',
|
|
29
|
+
},
|
|
30
|
+
],
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
<ConsentRoot
|
|
34
|
+
state={consent}
|
|
35
|
+
backendURL={backendURL}
|
|
36
|
+
initRoute={false}
|
|
37
|
+
scripts={scripts}
|
|
38
|
+
networkBlocker={networkBlocker}
|
|
39
|
+
>
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
The blocker runs in the browser only. It does not see requests your server
|
|
43
|
+
functions or server routes make.
|
|
44
|
+
|
|
45
|
+
## Match requests with rules
|
|
46
|
+
|
|
47
|
+
Each rule names a `domain` and the consent `category` a request needs. The
|
|
48
|
+
domain also matches its subdomains: `google-analytics.com` covers
|
|
49
|
+
`www.google-analytics.com`. `pathIncludes` narrows the rule to paths that
|
|
50
|
+
contain a substring, and `methods` narrows it to HTTP methods. A request is
|
|
51
|
+
blocked when a matching rule's condition is not met by the visitor's
|
|
52
|
+
effective permissions.
|
|
53
|
+
|
|
54
|
+
`category` takes the same conditions as scripts:
|
|
55
|
+
|
|
56
|
+
```ts
|
|
57
|
+
{ category: 'measurement' }
|
|
58
|
+
{ category: { and: ['measurement', 'marketing'] } }
|
|
59
|
+
{ category: { or: ['measurement', 'marketing'] } }
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Add `vendor` to also block the request while the visitor has turned that
|
|
63
|
+
vendor off. IAB TCF rules use `vendorId` and the `iab*` purpose fields
|
|
64
|
+
instead.
|
|
65
|
+
|
|
66
|
+
| Option | Default | Purpose |
|
|
67
|
+
| -------------------- | -------- | ------------------------------------------------------------ |
|
|
68
|
+
| `rules` | required | Rules described above |
|
|
69
|
+
| `enabled` | `true` | Set `false` to keep the rules but stop blocking |
|
|
70
|
+
| `logBlockedRequests` | `true` | Log each blocked request with `console.warn` |
|
|
71
|
+
| `onRequestBlocked` | none | Called with `{ method, url, rule }` for each blocked request |
|
|
72
|
+
|
|
73
|
+
## What a blocked request looks like
|
|
74
|
+
|
|
75
|
+
The blocker wraps `window.fetch` and `XMLHttpRequest`. A blocked `fetch`
|
|
76
|
+
resolves to a `451` response with the status text
|
|
77
|
+
`Request blocked by consent`, and nothing is sent. A blocked XHR is aborted
|
|
78
|
+
and fires an `error` event. Requests that match no rule are not delayed.
|
|
79
|
+
|
|
80
|
+
## When blocking starts
|
|
81
|
+
|
|
82
|
+
The provider holds matching requests from its first render in the browser,
|
|
83
|
+
before any of its children render or run effects. That covers requests from
|
|
84
|
+
child components, including their mount effects, and from effects in
|
|
85
|
+
components rendered next to the provider. The blocker module itself loads
|
|
86
|
+
after mount and decides each held request. Apps without `networkBlocker` do
|
|
87
|
+
not download it.
|
|
88
|
+
|
|
89
|
+
The standalone `useNetworkBlocker` hook works the same way from the first
|
|
90
|
+
render of the component that calls it. That render patches `fetch` and
|
|
91
|
+
`XMLHttpRequest`. If React throws the render away and never commits it, the
|
|
92
|
+
hold ends after 10 seconds. Nothing checked consent for the requests it held,
|
|
93
|
+
so they fail the way the blocker fails a blocked request: a 451 response for
|
|
94
|
+
`fetch`, a failed XHR. The same happens when the component unmounts before
|
|
95
|
+
the blocker loads.
|
|
96
|
+
|
|
97
|
+
While consent is unknown, a matching request that would be blocked waits
|
|
98
|
+
instead of failing. Consent is unknown until the policy has loaded, which is
|
|
99
|
+
also when a returning visitor's stored choice takes effect. The request is
|
|
100
|
+
then sent if the choice allows it and blocked otherwise. If the policy fails
|
|
101
|
+
to load, optional categories stay denied and waiting requests are blocked.
|
|
102
|
+
If the policy request never finishes, they keep waiting and are never sent.
|
|
103
|
+
|
|
104
|
+
A synchronous XHR cannot wait. Before the blocker module has loaded, a
|
|
105
|
+
matching one throws a `NetworkError` from `send()`. After that, one that
|
|
106
|
+
consent does not allow yet is blocked.
|
|
107
|
+
|
|
108
|
+
## What the network blocker cannot stop
|
|
109
|
+
|
|
110
|
+
The blocker only sees requests made after the provider starts rendering in
|
|
111
|
+
the browser, through `fetch` or `XMLHttpRequest`. It cannot stop:
|
|
112
|
+
|
|
113
|
+
* Code that runs before the provider renders: inline scripts in the HTML,
|
|
114
|
+
third-party tags in `<head>`, scripts loaded before hydration (such as
|
|
115
|
+
`next/script` with `beforeInteractive`), and client modules that evaluate
|
|
116
|
+
earlier. Webpack builds evaluate a route's client component modules when
|
|
117
|
+
its chunk loads, so their top-level code runs first. Turbopack evaluates a
|
|
118
|
+
client component module when its first element renders, which inside the
|
|
119
|
+
provider is after blocking starts.
|
|
120
|
+
* Code that saved its own reference to `fetch` or `XMLHttpRequest` before the
|
|
121
|
+
provider rendered.
|
|
122
|
+
* `navigator.sendBeacon`, `WebSocket`, `EventSource`, and requests made by
|
|
123
|
+
`<img>`, `<script>` or `<iframe>` elements, web workers and service
|
|
124
|
+
workers.
|
|
125
|
+
* With the standalone `useNetworkBlocker` hook instead of the provider
|
|
126
|
+
option, requests sent before the component that calls it renders.
|
|
127
|
+
Blocking starts in that component's first render, not the provider's.
|
|
128
|
+
|
|
129
|
+
Keep tracking calls out of that window:
|
|
130
|
+
|
|
131
|
+
* Send them from an effect or an event handler, never at module top level.
|
|
132
|
+
* Load vendor SDKs through `scripts` instead of a `<script>` tag or
|
|
133
|
+
`next/script`, so they wait for consent before they run at all.
|
|
134
|
+
* Check `useConsent('measurement')` (or the category you need) before you
|
|
135
|
+
call a vendor from your own code, and treat the blocker as a backstop.
|
|
136
|
+
|
|
137
|
+
## Verify the blocked requests
|
|
138
|
+
|
|
139
|
+
Open DevTools Network in a private window, under a policy that asks for
|
|
140
|
+
consent.
|
|
141
|
+
|
|
142
|
+
1. Before a choice, requests matching a rule are absent, and the console logs
|
|
143
|
+
each blocked request.
|
|
144
|
+
2. Allow the rule's category and save. Matching requests go out.
|
|
145
|
+
3. Reject, reload, and check that they stay absent.
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Scripts
|
|
3
|
+
description: Load vendor scripts by consent category in a TanStack Start app
|
|
4
|
+
with ConsentRoot, and clear stored data or reload the page when a visitor
|
|
5
|
+
withdraws consent.
|
|
6
|
+
group: frameworks
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Register vendor scripts
|
|
10
|
+
|
|
11
|
+
Pass vendor loaders to `ConsentRoot` through its `scripts` prop. `ConsentRoot`
|
|
12
|
+
loads each script in the browser when its category is allowed and removes it
|
|
13
|
+
when the category is denied. The [quickstart](https://c15t.com/docs/frameworks/tanstack-start/quickstart)
|
|
14
|
+
registers PostHog and X Pixel in `src/scripts.ts`:
|
|
15
|
+
|
|
16
|
+
```ts title="src/scripts.ts"
|
|
17
|
+
import { posthog } from '@c15t/integrations/posthog';
|
|
18
|
+
import { xPixel } from '@c15t/integrations/x-pixel';
|
|
19
|
+
|
|
20
|
+
export const scripts = [
|
|
21
|
+
posthog({
|
|
22
|
+
id: 'phc_your_project_key',
|
|
23
|
+
initOptions: { cookieless_mode: 'never' },
|
|
24
|
+
loadMode: 'after-consent',
|
|
25
|
+
region: 'eu',
|
|
26
|
+
}),
|
|
27
|
+
xPixel({ pixelId: 'your-pixel-id' }),
|
|
28
|
+
];
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
The root route imports that module and passes it on:
|
|
32
|
+
|
|
33
|
+
```tsx title="src/routes/__root.tsx"
|
|
34
|
+
import { scripts } from '../scripts';
|
|
35
|
+
|
|
36
|
+
<ConsentRoot
|
|
37
|
+
state={consent}
|
|
38
|
+
backendURL={backendURL}
|
|
39
|
+
initRoute={false}
|
|
40
|
+
scripts={scripts}
|
|
41
|
+
>
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Import vendor helpers in route modules, not in a server function. A server
|
|
45
|
+
function's return value must be serializable, and vendor loaders contain
|
|
46
|
+
functions. Scripts only load in the browser, so importing them in the root
|
|
47
|
+
route adds nothing to the server HTML.
|
|
48
|
+
|
|
49
|
+
Each helper from `@c15t/integrations` sets its own category and a stable `id`.
|
|
50
|
+
Remove every other loader for the same vendor, such as a `scripts` entry in a
|
|
51
|
+
route's `head()` or an SDK you initialize at module level, so the vendor loads
|
|
52
|
+
once and only through c15t. [Integrations](../../integrations/overview.md) lists
|
|
53
|
+
every helper, and [building integrations](../../integrations/building-integrations.md)
|
|
54
|
+
covers a vendor without one.
|
|
55
|
+
|
|
56
|
+
## What happens when consent changes
|
|
57
|
+
|
|
58
|
+
* **Allowed.** The script loads, or its SDK starts, the first time its category
|
|
59
|
+
is allowed. With an awaited root loader, a returning visitor's allowed
|
|
60
|
+
scripts start right after hydration.
|
|
61
|
+
* **Denied later.** The script element is removed. Code that already ran keeps
|
|
62
|
+
running, so after the visitor turns off a category they had allowed, the page
|
|
63
|
+
reloads once the save finishes. Set `reloadOnConsentRevoked: false` in
|
|
64
|
+
`ConsentRoot`'s `options` only if every gated vendor stops itself.
|
|
65
|
+
* **`alwaysLoad` helpers.** Some integrations, such as Google Consent Mode,
|
|
66
|
+
load before consent and pass the visitor's choice to the vendor. Read the
|
|
67
|
+
vendor's guide; a category on a script does not always mean zero requests.
|
|
68
|
+
|
|
69
|
+
## Embeds and other requests
|
|
70
|
+
|
|
71
|
+
Scripts cover vendor code c15t loads for you. For the rest:
|
|
72
|
+
|
|
73
|
+
* [Embeds](./embeds.md) keeps iframes out of the
|
|
74
|
+
page with `ConsentGate` or the iframe blocker.
|
|
75
|
+
* [Network blocker](./network-blocker.md) holds
|
|
76
|
+
`fetch` and XHR calls that match a rule until their category is allowed.
|
|
77
|
+
|
|
78
|
+
## Clear stored data after revocation
|
|
79
|
+
|
|
80
|
+
Removing a script does not delete the cookies or storage entries it wrote. Pass
|
|
81
|
+
`clearOnRevocation` to `ConsentRoot` to delete the entries you list for each
|
|
82
|
+
category when it is denied. `ConsentRoot` reads it once, when it mounts.
|
|
83
|
+
[Clear on revocation](https://c15t.com/docs/frameworks/tanstack-start/clear-on-revocation) covers
|
|
84
|
+
configuration and browser limits.
|
|
85
|
+
|
|
86
|
+
## Let visitors turn off one vendor
|
|
87
|
+
|
|
88
|
+
A visitor can allow marketing and still switch off one vendor in it. Pass
|
|
89
|
+
`vendors` to `ConsentRoot`. Helpers from `@c15t/integrations` already carry
|
|
90
|
+
their vendor slug. See [vendor consent](https://c15t.com/docs/frameworks/tanstack-start/vendor-consent).
|
|
91
|
+
|
|
92
|
+
## Check the scripts
|
|
93
|
+
|
|
94
|
+
Open DevTools Network in a private window, filter by each vendor's domain and
|
|
95
|
+
reload.
|
|
96
|
+
|
|
97
|
+
1. Before a choice under an opt-in policy, no vendor script loads. The page
|
|
98
|
+
source has no vendor `<script>` tags.
|
|
99
|
+
2. Allow one category. Only that category's vendors load.
|
|
100
|
+
3. Turn the category off. The page reloads and the vendor stays absent.
|
|
101
|
+
4. Reload again. The rejection holds, and the server HTML shows no banner.
|
|
102
|
+
|
|
103
|
+
[Verify consent](../../guides/verify-consent.md) has the full checklist.
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Embeds
|
|
3
|
+
description: Keep YouTube videos, maps and other iframes out of a Vue page until
|
|
4
|
+
their consent category is allowed, with ConsentGate or the iframe blocker.
|
|
5
|
+
group: frameworks
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Pick a method
|
|
9
|
+
|
|
10
|
+
| Method | Use it when |
|
|
11
|
+
| ------------------ | ------------------------------------------------------------------------------------------------------------- |
|
|
12
|
+
| `ConsentGate` | You render the iframe in a Vue component and want a placeholder with a way to open preferences. |
|
|
13
|
+
| The iframe blocker | The iframe comes from markup you do not render with a component, such as CMS content, or you want no wrapper. |
|
|
14
|
+
|
|
15
|
+
Both remove the iframe's `src` until the category is allowed, so the embed's
|
|
16
|
+
host receives no request before consent.
|
|
17
|
+
|
|
18
|
+
## Gate an embed with ConsentGate
|
|
19
|
+
|
|
20
|
+
Wrap the iframe in [`ConsentGate`](https://c15t.com/docs/frameworks/vue/components/consent-gate)
|
|
21
|
+
from `c15t/vue/runtime/components/consent-gate.vue`:
|
|
22
|
+
|
|
23
|
+
```vue title="src/VideoEmbed.vue"
|
|
24
|
+
<script setup lang="ts">
|
|
25
|
+
import ConsentGate from 'c15t/vue/runtime/components/consent-gate.vue';
|
|
26
|
+
import ConsentPreferencesLink from 'c15t/vue/runtime/components/consent-preferences-link.vue';
|
|
27
|
+
</script>
|
|
28
|
+
|
|
29
|
+
<template>
|
|
30
|
+
<ConsentGate category="measurement">
|
|
31
|
+
<iframe
|
|
32
|
+
src="https://www.youtube-nocookie.com/embed/czTksCF6X8Y"
|
|
33
|
+
title="YouTube video"
|
|
34
|
+
loading="lazy"
|
|
35
|
+
allowfullscreen
|
|
36
|
+
/>
|
|
37
|
+
<template #placeholder>
|
|
38
|
+
<p>Allow measurement to load this YouTube video.</p>
|
|
39
|
+
<ConsentPreferencesLink>Choose video permissions</ConsentPreferencesLink>
|
|
40
|
+
</template>
|
|
41
|
+
</ConsentGate>
|
|
42
|
+
</template>
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
The iframe does not exist in the page until the visitor allows measurement.
|
|
46
|
+
The `placeholder` slot shows a message and a
|
|
47
|
+
[`ConsentPreferencesLink`](https://c15t.com/docs/frameworks/vue/components/consent-preferences-link)
|
|
48
|
+
until then. When the visitor withdraws measurement, Vue removes the iframe.
|
|
49
|
+
|
|
50
|
+
Pick the category that matches what the embed does. The example uses
|
|
51
|
+
measurement for YouTube because its player measures views. A map or chat
|
|
52
|
+
widget usually belongs under functionality or experience.
|
|
53
|
+
|
|
54
|
+
## Gate an iframe with the iframe blocker
|
|
55
|
+
|
|
56
|
+
The Vue plugin runs the iframe blocker by default. Give an iframe `data-src`
|
|
57
|
+
instead of `src`, and a `data-category`:
|
|
58
|
+
|
|
59
|
+
```vue title="src/MapEmbed.vue"
|
|
60
|
+
<template>
|
|
61
|
+
<iframe
|
|
62
|
+
data-src="https://www.google.com/maps/embed?pb=YOUR_EMBED_ID"
|
|
63
|
+
data-category="functionality"
|
|
64
|
+
title="Store map"
|
|
65
|
+
/>
|
|
66
|
+
</template>
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
c15t sets `src` from `data-src` when the category is allowed and removes it
|
|
70
|
+
when the category is withdrawn. It watches the page, so iframes added later,
|
|
71
|
+
for example by a route change, are handled too. The iframe element stays in
|
|
72
|
+
the page while blocked, empty. Add `data-vendor` with a vendor ID to also
|
|
73
|
+
block the iframe while the visitor has turned that vendor off.
|
|
74
|
+
|
|
75
|
+
Pass `iframeBlocker: false` to the plugin to turn the blocker off.
|
|
76
|
+
|
|
77
|
+
## Verify
|
|
78
|
+
|
|
79
|
+
Open the page in a private window with the Network tab open, under a policy
|
|
80
|
+
that asks for consent. No request goes to `youtube-nocookie.com` or
|
|
81
|
+
`google.com/maps`. Allow the category and save: the iframe loads. Withdraw it
|
|
82
|
+
and save: the page reloads without the embed. The
|
|
83
|
+
[YouTube](../../integrations/youtube.md) and
|
|
84
|
+
[Google Maps](../../integrations/google-maps.md) guides cover sizing and titles.
|