@c15t/scripts 2.2.0 → 3.0.0-alpha.0
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 +74 -48
- package/README.md +3 -3
- package/dist/e2e-test-utils.js +60 -24
- package/dist/engine/compile.js +45 -45
- package/dist/engine/runtime.js +119 -119
- package/dist/registry.js +186 -176
- package/dist/resolve.js +12 -12
- package/dist/vendors/_shared/attributes.js +5 -5
- package/dist/vendors/_shared/google-consent.js +10 -10
- package/dist/vendors/_shared/install-builders.js +9 -9
- package/dist/vendors/_shared/script-url.js +12 -12
- package/dist/vendors/ads-and-pixels/linkedin-insights.js +16 -16
- package/dist/vendors/ads-and-pixels/meta-pixel.js +82 -82
- package/dist/vendors/ads-and-pixels/microsoft-uet.js +57 -57
- package/dist/vendors/ads-and-pixels/openai-pixel.js +88 -0
- package/dist/vendors/ads-and-pixels/reddit-pixel.js +39 -39
- package/dist/vendors/ads-and-pixels/snapchat-pixel.js +25 -25
- package/dist/vendors/ads-and-pixels/tiktok-pixel.js +31 -31
- package/dist/vendors/ads-and-pixels/x-pixel.js +17 -17
- package/dist/vendors/analytics/adobe-analytics.js +17 -17
- package/dist/vendors/analytics/ahrefs-analytics.js +8 -8
- package/dist/vendors/analytics/amplitude.js +39 -39
- package/dist/vendors/analytics/clearbit.js +11 -11
- package/dist/vendors/analytics/cloudflare-web-analytics.js +13 -13
- package/dist/vendors/analytics/databuddy.js +45 -45
- package/dist/vendors/analytics/fathom-analytics.js +15 -15
- package/dist/vendors/analytics/google-tag.js +23 -23
- package/dist/vendors/analytics/heap.js +37 -37
- package/dist/vendors/analytics/hightouch.js +30 -30
- package/dist/vendors/analytics/hotjar.js +14 -14
- package/dist/vendors/analytics/logrocket.js +24 -24
- package/dist/vendors/analytics/matomo-analytics.js +51 -51
- package/dist/vendors/analytics/microsoft-clarity.js +31 -31
- package/dist/vendors/analytics/mixpanel-analytics.js +31 -31
- package/dist/vendors/analytics/pirsch.js +27 -27
- package/dist/vendors/analytics/plausible-analytics.js +24 -24
- package/dist/vendors/analytics/posthog.js +84 -79
- package/dist/vendors/analytics/promptwatch.js +8 -8
- package/dist/vendors/analytics/rudderstack.js +50 -50
- package/dist/vendors/analytics/rybbit-analytics.js +30 -30
- package/dist/vendors/analytics/segment.js +16 -16
- package/dist/vendors/analytics/umami-analytics.js +16 -16
- package/dist/vendors/analytics/vercel-analytics.js +22 -22
- package/dist/vendors/functional/crisp.js +49 -51
- package/dist/vendors/functional/intercom.js +18 -18
- package/dist/vendors/tag-managers/google-tag-manager.js +20 -20
- package/dist-types/__tests__/helpers.d.ts +10 -10
- package/dist-types/engine/compile.d.ts +2 -2
- package/dist-types/engine/runtime.d.ts +3 -3
- package/dist-types/registry.d.ts +182 -173
- package/dist-types/resolve.d.ts +2 -2
- package/dist-types/types.d.ts +2 -2
- package/dist-types/vendors/_shared/attributes.d.ts +2 -2
- package/dist-types/vendors/_shared/google-consent.d.ts +2 -2
- package/dist-types/vendors/_shared/install-builders.d.ts +1 -1
- package/dist-types/vendors/_shared/script-url.d.ts +6 -6
- package/dist-types/vendors/ads-and-pixels/linkedin-insights.d.ts +14 -14
- package/dist-types/vendors/ads-and-pixels/meta-pixel.d.ts +27 -27
- package/dist-types/vendors/ads-and-pixels/microsoft-uet.d.ts +40 -40
- package/dist-types/vendors/ads-and-pixels/openai-pixel.d.ts +211 -0
- package/dist-types/vendors/ads-and-pixels/reddit-pixel.d.ts +28 -29
- package/dist-types/vendors/ads-and-pixels/snapchat-pixel.d.ts +23 -23
- package/dist-types/vendors/ads-and-pixels/tiktok-pixel.d.ts +22 -22
- package/dist-types/vendors/ads-and-pixels/x-pixel.d.ts +15 -15
- package/dist-types/vendors/analytics/adobe-analytics.d.ts +3 -3
- package/dist-types/vendors/analytics/ahrefs-analytics.d.ts +5 -5
- package/dist-types/vendors/analytics/amplitude.d.ts +24 -24
- package/dist-types/vendors/analytics/clearbit.d.ts +5 -5
- package/dist-types/vendors/analytics/cloudflare-web-analytics.d.ts +6 -6
- package/dist-types/vendors/analytics/databuddy.d.ts +34 -31
- package/dist-types/vendors/analytics/fathom-analytics.d.ts +8 -8
- package/dist-types/vendors/analytics/google-tag.d.ts +17 -17
- package/dist-types/vendors/analytics/heap.d.ts +17 -17
- package/dist-types/vendors/analytics/hightouch.d.ts +15 -15
- package/dist-types/vendors/analytics/hotjar.d.ts +9 -9
- package/dist-types/vendors/analytics/logrocket.d.ts +11 -11
- package/dist-types/vendors/analytics/matomo-analytics.d.ts +3 -3
- package/dist-types/vendors/analytics/microsoft-clarity.d.ts +12 -13
- package/dist-types/vendors/analytics/mixpanel-analytics.d.ts +20 -20
- package/dist-types/vendors/analytics/pirsch.d.ts +10 -10
- package/dist-types/vendors/analytics/plausible-analytics.d.ts +13 -13
- package/dist-types/vendors/analytics/posthog.d.ts +35 -32
- package/dist-types/vendors/analytics/promptwatch.d.ts +5 -5
- package/dist-types/vendors/analytics/rudderstack.d.ts +16 -16
- package/dist-types/vendors/analytics/rybbit-analytics.d.ts +15 -15
- package/dist-types/vendors/analytics/segment.d.ts +11 -11
- package/dist-types/vendors/analytics/umami-analytics.d.ts +9 -9
- package/dist-types/vendors/analytics/vercel-analytics.d.ts +13 -13
- package/dist-types/vendors/functional/crisp.d.ts +9 -9
- package/dist-types/vendors/functional/intercom.d.ts +12 -12
- package/dist-types/vendors/tag-managers/google-tag-manager.d.ts +16 -16
- package/docs/README.md +74 -48
- package/docs/assets/v3/brand-bar.png +0 -0
- package/docs/assets/v3/brand-card.png +0 -0
- package/docs/assets/v3/choice-wall.png +0 -0
- package/docs/assets/v3/mobile-card.png +0 -0
- package/docs/assets/v3/preferences.png +0 -0
- package/docs/customization/overview.md +45 -0
- package/docs/customization/recipes.md +79 -0
- package/docs/customization/slots.md +55 -0
- package/docs/customization/tokens.md +76 -0
- package/docs/customization/translations.md +49 -0
- package/docs/frameworks/javascript/script-loader.md +30 -339
- package/docs/frameworks/next/script-loader.md +134 -467
- package/docs/frameworks/react/script-loader.md +35 -535
- package/docs/guides/consent-state.md +60 -0
- package/docs/guides/data-fetching.md +163 -0
- package/docs/guides/deployment-modes.md +63 -0
- package/docs/guides/troubleshooting.md +68 -0
- package/docs/guides/verify-consent.md +62 -0
- package/docs/integrations/adobe-analytics.md +239 -105
- package/docs/integrations/ahrefs-analytics.md +238 -104
- package/docs/integrations/amplitude.md +219 -157
- package/docs/integrations/building-integrations.md +32 -224
- package/docs/integrations/clearbit.md +247 -86
- package/docs/integrations/cloudflare-web-analytics.md +250 -84
- package/docs/integrations/crisp.md +251 -97
- package/docs/integrations/databuddy.md +259 -153
- package/docs/integrations/fathom-analytics.md +239 -96
- package/docs/integrations/google-maps.md +328 -207
- package/docs/integrations/google-tag-manager.md +248 -96
- package/docs/integrations/google-tag.md +261 -90
- package/docs/integrations/heap.md +222 -149
- package/docs/integrations/hightouch.md +225 -131
- package/docs/integrations/hotjar.md +239 -90
- package/docs/integrations/intercom.md +239 -98
- package/docs/integrations/linkedin-insights.md +243 -113
- package/docs/integrations/logrocket.md +241 -123
- package/docs/integrations/matomo-analytics.md +256 -111
- package/docs/integrations/meta-pixel.md +197 -324
- package/docs/integrations/microsoft-clarity.md +233 -114
- package/docs/integrations/microsoft-uet.md +245 -110
- package/docs/integrations/mixpanel-analytics.md +252 -87
- package/docs/integrations/openai-pixel.md +441 -0
- package/docs/integrations/overview.md +95 -133
- package/docs/integrations/pirsch.md +249 -96
- package/docs/integrations/plausible-analytics.md +241 -100
- package/docs/integrations/posthog.md +353 -214
- package/docs/integrations/promptwatch.md +251 -81
- package/docs/integrations/reddit-pixel.md +226 -173
- package/docs/integrations/rudderstack.md +244 -187
- package/docs/integrations/rybbit-analytics.md +244 -91
- package/docs/integrations/segment.md +238 -92
- package/docs/integrations/snapchat-pixel.md +240 -110
- package/docs/integrations/tiktok-pixel.md +249 -81
- package/docs/integrations/umami-analytics.md +242 -95
- package/docs/integrations/vercel-analytics.md +242 -90
- package/docs/integrations/x-pixel.md +238 -104
- package/docs/integrations/youtube.md +354 -142
- package/docs/upgrade-v3.md +334 -0
- package/package.json +85 -78
- package/readme.json +2 -2
- package/dist/e2e-test-utils.cjs +0 -166
- package/dist/engine/compile.cjs +0 -130
- package/dist/engine/runtime.cjs +0 -475
- package/dist/registry.cjs +0 -423
- package/dist/resolve.cjs +0 -71
- package/dist/types.cjs +0 -69
- package/dist/vendors/_shared/attributes.cjs +0 -55
- package/dist/vendors/_shared/google-consent.cjs +0 -69
- package/dist/vendors/_shared/install-builders.cjs +0 -59
- package/dist/vendors/_shared/script-url.cjs +0 -78
- package/dist/vendors/ads-and-pixels/linkedin-insights.cjs +0 -89
- package/dist/vendors/ads-and-pixels/meta-pixel.cjs +0 -206
- package/dist/vendors/ads-and-pixels/microsoft-uet.cjs +0 -151
- package/dist/vendors/ads-and-pixels/reddit-pixel.cjs +0 -151
- package/dist/vendors/ads-and-pixels/snapchat-pixel.cjs +0 -131
- package/dist/vendors/ads-and-pixels/tiktok-pixel.cjs +0 -130
- package/dist/vendors/ads-and-pixels/x-pixel.cjs +0 -92
- package/dist/vendors/analytics/adobe-analytics.cjs +0 -90
- package/dist/vendors/analytics/ahrefs-analytics.cjs +0 -68
- package/dist/vendors/analytics/amplitude.cjs +0 -193
- package/dist/vendors/analytics/clearbit.cjs +0 -69
- package/dist/vendors/analytics/cloudflare-web-analytics.cjs +0 -73
- package/dist/vendors/analytics/databuddy.cjs +0 -144
- package/dist/vendors/analytics/fathom-analytics.cjs +0 -76
- package/dist/vendors/analytics/google-tag.cjs +0 -107
- package/dist/vendors/analytics/heap.cjs +0 -181
- package/dist/vendors/analytics/hightouch.cjs +0 -153
- package/dist/vendors/analytics/hotjar.cjs +0 -85
- package/dist/vendors/analytics/logrocket.cjs +0 -99
- package/dist/vendors/analytics/matomo-analytics.cjs +0 -232
- package/dist/vendors/analytics/microsoft-clarity.cjs +0 -138
- package/dist/vendors/analytics/mixpanel-analytics.cjs +0 -134
- package/dist/vendors/analytics/pirsch.cjs +0 -108
- package/dist/vendors/analytics/plausible-analytics.cjs +0 -122
- package/dist/vendors/analytics/posthog.cjs +0 -236
- package/dist/vendors/analytics/promptwatch.cjs +0 -70
- package/dist/vendors/analytics/rudderstack.cjs +0 -227
- package/dist/vendors/analytics/rybbit-analytics.cjs +0 -104
- package/dist/vendors/analytics/segment.cjs +0 -97
- package/dist/vendors/analytics/umami-analytics.cjs +0 -80
- package/dist/vendors/analytics/vercel-analytics.cjs +0 -94
- package/dist/vendors/functional/crisp.cjs +0 -143
- package/dist/vendors/functional/intercom.cjs +0 -89
- package/dist/vendors/tag-managers/google-tag-manager.cjs +0 -100
- package/docs/shared/react/guides/script-loader.md +0 -311
|
@@ -1,193 +1,405 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: YouTube
|
|
3
|
-
description:
|
|
4
|
-
|
|
5
|
-
icon: youtube
|
|
3
|
+
description: Gate YouTube embeds with c15t v3 in Next.js, TanStack Start, React,
|
|
4
|
+
Nuxt, Vue, Astro, Svelte, SvelteKit or JavaScript.
|
|
6
5
|
group: integrations
|
|
7
6
|
---
|
|
8
|
-
`YouTubeEmbed` is a renderable integration for React and Next.js. It uses c15t's
|
|
9
|
-
`Frame` consent boundary, so the YouTube iframe is not mounted and no YouTube
|
|
10
|
-
request is made until the configured consent category is allowed.
|
|
11
7
|
|
|
12
|
-
|
|
13
|
-
standard iframe embeds and keeps script readiness separate from iframe gating.
|
|
8
|
+
## Configure the video
|
|
14
9
|
|
|
15
|
-
|
|
10
|
+
Use the ID of an embeddable video and the category matching its purpose in your
|
|
11
|
+
policy. The current v3 adapters use consent-gated iframes rather than the v2
|
|
12
|
+
`YouTubeEmbed` convenience component.
|
|
16
13
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
14
|
+
```ts title="src/embed-config.ts"
|
|
15
|
+
export const embedCategory = 'marketing' as const;
|
|
16
|
+
export const embedURL = 'https://www.youtube-nocookie.com/embed/VIDEO_ID?playsinline=1';
|
|
17
|
+
export const embedTitle = 'Product walkthrough';
|
|
18
|
+
export const embedAspectRatio = '16 / 9';
|
|
19
|
+
```
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
Replace `VIDEO_ID` before running the example.
|
|
22
|
+
|
|
23
|
+
## Add the embed to your framework
|
|
24
|
+
|
|
25
|
+
Create `src/embed-config.ts` using the configuration on this page, then select
|
|
26
|
+
your framework. Keep the existing [consent setup](https://c15t.com/docs/frameworks), its policy
|
|
27
|
+
and preferences UI. These examples do not create a second consent provider.
|
|
28
|
+
|
|
29
|
+
**Next.js**
|
|
30
|
+
|
|
31
|
+
Render this component inside your existing consent boundary or provider.
|
|
32
|
+
|
|
33
|
+
```tsx title="src/consent-embed.tsx"
|
|
34
|
+
'use client';
|
|
22
35
|
|
|
23
|
-
|
|
24
|
-
import {
|
|
36
|
+
import { Frame } from 'c15t/next';
|
|
37
|
+
import { embedCategory, embedURL, embedTitle, embedAspectRatio } from './embed-config';
|
|
25
38
|
|
|
26
|
-
export function
|
|
39
|
+
export function ConsentEmbed() {
|
|
27
40
|
return (
|
|
28
|
-
<
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
41
|
+
<Frame category={embedCategory}>
|
|
42
|
+
<iframe
|
|
43
|
+
src={embedURL}
|
|
44
|
+
title={embedTitle}
|
|
45
|
+
loading="lazy"
|
|
46
|
+
allowFullScreen
|
|
47
|
+
style={{ width: '100%', aspectRatio: embedAspectRatio, minHeight: 200, border: 0 }}
|
|
48
|
+
/>
|
|
49
|
+
</Frame>
|
|
34
50
|
);
|
|
35
51
|
}
|
|
36
52
|
```
|
|
37
53
|
|
|
38
|
-
|
|
54
|
+
`Frame` keeps the iframe absent while permission is denied and removes it
|
|
55
|
+
on revocation. Keep your existing consent styles and preferences dialog.
|
|
39
56
|
|
|
40
|
-
|
|
41
|
-
|
|
57
|
+
**TanStack Start**
|
|
58
|
+
|
|
59
|
+
Render this component inside your existing consent boundary or provider.
|
|
42
60
|
|
|
43
|
-
|
|
61
|
+
```tsx title="src/consent-embed.tsx"
|
|
62
|
+
import { Frame } from 'c15t/tanstack-start';
|
|
63
|
+
import { embedCategory, embedURL, embedTitle, embedAspectRatio } from './embed-config';
|
|
44
64
|
|
|
45
|
-
export function
|
|
65
|
+
export function ConsentEmbed() {
|
|
46
66
|
return (
|
|
47
|
-
<
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
67
|
+
<Frame category={embedCategory}>
|
|
68
|
+
<iframe
|
|
69
|
+
src={embedURL}
|
|
70
|
+
title={embedTitle}
|
|
71
|
+
loading="lazy"
|
|
72
|
+
allowFullScreen
|
|
73
|
+
style={{ width: '100%', aspectRatio: embedAspectRatio, minHeight: 200, border: 0 }}
|
|
74
|
+
/>
|
|
75
|
+
</Frame>
|
|
53
76
|
);
|
|
54
77
|
}
|
|
55
78
|
```
|
|
56
79
|
|
|
57
|
-
`
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
<YouTubeEmbed
|
|
82
|
-
consentCategory="marketing"
|
|
83
|
-
params={{
|
|
84
|
-
autoplay: false,
|
|
85
|
-
controls: true,
|
|
86
|
-
playsinline: true,
|
|
87
|
-
rel: false,
|
|
88
|
-
}}
|
|
89
|
-
start={36}
|
|
90
|
-
title="Quarterly product update"
|
|
91
|
-
videoId="dQw4w9WgXcQ"
|
|
92
|
-
/>
|
|
80
|
+
`Frame` keeps the iframe absent while permission is denied and removes it
|
|
81
|
+
on revocation. Keep your existing consent styles and preferences dialog.
|
|
82
|
+
|
|
83
|
+
**React**
|
|
84
|
+
|
|
85
|
+
Render this component inside your existing consent boundary or provider.
|
|
86
|
+
|
|
87
|
+
```tsx title="src/consent-embed.tsx"
|
|
88
|
+
import { Frame } from 'c15t/react';
|
|
89
|
+
import { embedCategory, embedURL, embedTitle, embedAspectRatio } from './embed-config';
|
|
90
|
+
|
|
91
|
+
export function ConsentEmbed() {
|
|
92
|
+
return (
|
|
93
|
+
<Frame category={embedCategory}>
|
|
94
|
+
<iframe
|
|
95
|
+
src={embedURL}
|
|
96
|
+
title={embedTitle}
|
|
97
|
+
loading="lazy"
|
|
98
|
+
allowFullScreen
|
|
99
|
+
style={{ width: '100%', aspectRatio: embedAspectRatio, minHeight: 200, border: 0 }}
|
|
100
|
+
/>
|
|
101
|
+
</Frame>
|
|
102
|
+
);
|
|
103
|
+
}
|
|
93
104
|
```
|
|
94
105
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
106
|
+
`Frame` keeps the iframe absent while permission is denied and removes it
|
|
107
|
+
on revocation. Keep your existing consent styles and preferences dialog.
|
|
108
|
+
|
|
109
|
+
**Nuxt**
|
|
110
|
+
|
|
111
|
+
Use the reactive snapshot from the existing consent runtime. The Nuxt module auto-imports the consent composables.
|
|
112
|
+
|
|
113
|
+
```vue title="app/components/ConsentEmbed.vue"
|
|
114
|
+
<script setup lang="ts">
|
|
115
|
+
import { embedCategory, embedURL, embedTitle, embedAspectRatio } from '../../src/embed-config';
|
|
116
|
+
|
|
117
|
+
const snapshot = useConsentSnapshot();
|
|
118
|
+
const activeUI = useConsentActiveUI();
|
|
119
|
+
</script>
|
|
120
|
+
|
|
121
|
+
<template>
|
|
122
|
+
<iframe
|
|
123
|
+
v-if="snapshot.effectivePermissions[embedCategory]"
|
|
124
|
+
:src="embedURL"
|
|
125
|
+
:title="embedTitle"
|
|
126
|
+
loading="lazy"
|
|
127
|
+
allowfullscreen
|
|
128
|
+
:style="{ width: '100%', aspectRatio: embedAspectRatio, minHeight: '200px', border: 0 }"
|
|
129
|
+
/>
|
|
130
|
+
<button v-else type="button" @click="activeUI = 'manager'">
|
|
131
|
+
Open privacy settings to view this content
|
|
132
|
+
</button>
|
|
133
|
+
</template>
|
|
134
|
+
```
|
|
98
135
|
|
|
99
|
-
|
|
100
|
-
`
|
|
136
|
+
Use `v-if` so a denied iframe is removed from the DOM. Hiding an existing
|
|
137
|
+
iframe with `v-show` or CSS does not prevent its requests.
|
|
138
|
+
|
|
139
|
+
**Vue**
|
|
140
|
+
|
|
141
|
+
Use the reactive snapshot from the existing consent runtime. The c15t Vue plugin must already be installed on this app.
|
|
142
|
+
|
|
143
|
+
```vue title="src/ConsentEmbed.vue"
|
|
144
|
+
<script setup lang="ts">
|
|
145
|
+
import { useConsentSnapshot, useConsentActiveUI } from 'c15t/vue/vue-plugin';
|
|
146
|
+
import { embedCategory, embedURL, embedTitle, embedAspectRatio } from './embed-config';
|
|
147
|
+
|
|
148
|
+
const snapshot = useConsentSnapshot();
|
|
149
|
+
const activeUI = useConsentActiveUI();
|
|
150
|
+
</script>
|
|
151
|
+
|
|
152
|
+
<template>
|
|
153
|
+
<iframe
|
|
154
|
+
v-if="snapshot.effectivePermissions[embedCategory]"
|
|
155
|
+
:src="embedURL"
|
|
156
|
+
:title="embedTitle"
|
|
157
|
+
loading="lazy"
|
|
158
|
+
allowfullscreen
|
|
159
|
+
:style="{ width: '100%', aspectRatio: embedAspectRatio, minHeight: '200px', border: 0 }"
|
|
160
|
+
/>
|
|
161
|
+
<button v-else type="button" @click="activeUI = 'manager'">
|
|
162
|
+
Open privacy settings to view this content
|
|
163
|
+
</button>
|
|
164
|
+
</template>
|
|
165
|
+
```
|
|
101
166
|
|
|
102
|
-
|
|
167
|
+
Use `v-if` so a denied iframe is removed from the DOM. Hiding an existing
|
|
168
|
+
iframe with `v-show` or CSS does not prevent its requests.
|
|
169
|
+
|
|
170
|
+
**Astro**
|
|
171
|
+
|
|
172
|
+
Use the shared browser helper below with Astro's existing page runtime.
|
|
173
|
+
Add this component to pages using your consent-enabled base layout:
|
|
174
|
+
|
|
175
|
+
```astro title="src/components/ConsentEmbed.astro"
|
|
176
|
+
<c15t-consent-embed style="display: block"></c15t-consent-embed>
|
|
177
|
+
|
|
178
|
+
<script>
|
|
179
|
+
import { getConsentClient } from '@c15t/astro/client';
|
|
180
|
+
import { mountConsentEmbed } from '../consent-embed';
|
|
181
|
+
|
|
182
|
+
class ConsentEmbed extends HTMLElement {
|
|
183
|
+
dispose?: () => void;
|
|
184
|
+
|
|
185
|
+
connect = () => {
|
|
186
|
+
if (this.dispose) return;
|
|
187
|
+
const client = getConsentClient();
|
|
188
|
+
if (!client) return;
|
|
189
|
+
this.dispose = mountConsentEmbed(
|
|
190
|
+
this,
|
|
191
|
+
client.runtime.kernel,
|
|
192
|
+
() => { void client.openDialog(); },
|
|
193
|
+
);
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
connectedCallback() {
|
|
197
|
+
document.addEventListener('DOMContentLoaded', this.connect, { once: true });
|
|
198
|
+
this.connect();
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
disconnectedCallback() {
|
|
202
|
+
document.removeEventListener('DOMContentLoaded', this.connect);
|
|
203
|
+
this.dispose?.();
|
|
204
|
+
this.dispose = undefined;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
103
207
|
|
|
104
|
-
|
|
208
|
+
if (!customElements.get('c15t-consent-embed')) {
|
|
209
|
+
customElements.define('c15t-consent-embed', ConsentEmbed);
|
|
210
|
+
}
|
|
211
|
+
</script>
|
|
212
|
+
```
|
|
105
213
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
214
|
+
The first mount waits for the page's module scripts, including c15t's boot
|
|
215
|
+
script. Later `ClientRouter` mounts reuse the existing runtime. Removing the
|
|
216
|
+
component unsubscribes and removes the iframe. Do not create another consent
|
|
217
|
+
runtime for this embed.
|
|
218
|
+
|
|
219
|
+
**Svelte**
|
|
220
|
+
|
|
221
|
+
Render this component inside the existing `ConsentManagerProvider`.
|
|
222
|
+
The provider from your quickstart supplies its consent state.
|
|
223
|
+
|
|
224
|
+
```svelte title="src/ConsentEmbed.svelte"
|
|
225
|
+
<script lang="ts">
|
|
226
|
+
import { Frame } from '@c15t/svelte';
|
|
227
|
+
import { embedCategory, embedURL, embedTitle, embedAspectRatio } from './embed-config';
|
|
228
|
+
</script>
|
|
229
|
+
|
|
230
|
+
<Frame category={embedCategory}>
|
|
231
|
+
<iframe
|
|
232
|
+
src={embedURL}
|
|
233
|
+
title={embedTitle}
|
|
234
|
+
loading="lazy"
|
|
235
|
+
allowfullscreen
|
|
236
|
+
style:width="100%"
|
|
237
|
+
style:aspect-ratio={embedAspectRatio}
|
|
238
|
+
style:min-height="200px"
|
|
239
|
+
style:border="0"
|
|
240
|
+
></iframe>
|
|
241
|
+
</Frame>
|
|
112
242
|
```
|
|
113
243
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
244
|
+
The Svelte `Frame` waits until the browser is mounted and the category is
|
|
245
|
+
allowed. Its default placeholder opens preferences. Revocation removes the
|
|
246
|
+
iframe.
|
|
247
|
+
|
|
248
|
+
**SvelteKit**
|
|
249
|
+
|
|
250
|
+
Render this component inside the existing `ConsentManagerProvider`.
|
|
251
|
+
Keep the SvelteKit root provider and its server prefetch unchanged.
|
|
252
|
+
|
|
253
|
+
```svelte title="src/lib/ConsentEmbed.svelte"
|
|
254
|
+
<script lang="ts">
|
|
255
|
+
import { Frame } from '@c15t/svelte';
|
|
256
|
+
import { embedCategory, embedURL, embedTitle, embedAspectRatio } from '../embed-config';
|
|
257
|
+
</script>
|
|
258
|
+
|
|
259
|
+
<Frame category={embedCategory}>
|
|
260
|
+
<iframe
|
|
261
|
+
src={embedURL}
|
|
262
|
+
title={embedTitle}
|
|
263
|
+
loading="lazy"
|
|
264
|
+
allowfullscreen
|
|
265
|
+
style:width="100%"
|
|
266
|
+
style:aspect-ratio={embedAspectRatio}
|
|
267
|
+
style:min-height="200px"
|
|
268
|
+
style:border="0"
|
|
269
|
+
></iframe>
|
|
270
|
+
</Frame>
|
|
271
|
+
```
|
|
117
272
|
|
|
118
|
-
|
|
273
|
+
The Svelte `Frame` waits until the browser is mounted and the category is
|
|
274
|
+
allowed. Its default placeholder opens preferences. Revocation removes the
|
|
275
|
+
iframe.
|
|
119
276
|
|
|
120
|
-
|
|
121
|
-
forwarded ref target the iframe itself.
|
|
277
|
+
**JavaScript**
|
|
122
278
|
|
|
123
|
-
|
|
124
|
-
|
|
279
|
+
Use the shared browser helper below with your existing kernel. Put an
|
|
280
|
+
empty container where the embed should appear:
|
|
125
281
|
|
|
126
|
-
```
|
|
127
|
-
<
|
|
128
|
-
consentCategory="marketing"
|
|
129
|
-
frameProps={{ style: { aspectRatio: '4 / 3', minHeight: 280 } }}
|
|
130
|
-
style={{ borderRadius: 4 }}
|
|
131
|
-
title="Product overview"
|
|
132
|
-
videoId="dQw4w9WgXcQ"
|
|
133
|
-
/>
|
|
282
|
+
```html
|
|
283
|
+
<div id="consent-embed"></div>
|
|
134
284
|
```
|
|
135
285
|
|
|
136
|
-
|
|
137
|
-
wrapper, has no border, and inherits the wrapper radius by default.
|
|
286
|
+
In your browser entry point, after creating the kernel:
|
|
138
287
|
|
|
139
|
-
|
|
288
|
+
```ts
|
|
289
|
+
import { mountConsentEmbed } from './consent-embed';
|
|
140
290
|
|
|
141
|
-
|
|
142
|
-
|
|
291
|
+
const container = document.querySelector<HTMLElement>('#consent-embed');
|
|
292
|
+
if (!container) throw new Error('Missing consent embed container');
|
|
143
293
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
294
|
+
const disposeEmbed = mountConsentEmbed(container, kernel, openPreferences);
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
`kernel` is the instance from your quickstart. `openPreferences` is your
|
|
298
|
+
application's function for showing its consent preferences UI. Call
|
|
299
|
+
`disposeEmbed()` when the page or component is destroyed. The helper observes
|
|
300
|
+
both the current snapshot and later changes.
|
|
301
|
+
|
|
302
|
+
## Browser helper for Astro and JavaScript
|
|
303
|
+
|
|
304
|
+
Only the Astro and JavaScript examples need this helper. It creates the iframe
|
|
305
|
+
when permission allows it, keeps an existing player mounted across unrelated
|
|
306
|
+
snapshot updates, and removes it on revocation.
|
|
307
|
+
|
|
308
|
+
```ts title="src/consent-embed.ts"
|
|
309
|
+
import { embedCategory, embedURL, embedTitle, embedAspectRatio } from './embed-config';
|
|
310
|
+
|
|
311
|
+
type EmbedKernel = {
|
|
312
|
+
getSnapshot: () => {
|
|
313
|
+
effectivePermissions: Record<typeof embedCategory, boolean>;
|
|
314
|
+
};
|
|
315
|
+
subscribe: (listener: () => void) => () => void;
|
|
316
|
+
};
|
|
317
|
+
|
|
318
|
+
export function mountConsentEmbed(
|
|
319
|
+
container: HTMLElement,
|
|
320
|
+
kernel: EmbedKernel,
|
|
321
|
+
openPreferences: () => void,
|
|
322
|
+
) {
|
|
323
|
+
const render = () => {
|
|
324
|
+
if (kernel.getSnapshot().effectivePermissions[embedCategory]) {
|
|
325
|
+
if (container.querySelector('iframe')) return;
|
|
326
|
+
const frame = document.createElement('iframe');
|
|
327
|
+
frame.src = embedURL;
|
|
328
|
+
frame.title = embedTitle;
|
|
329
|
+
frame.loading = 'lazy';
|
|
330
|
+
frame.allowFullscreen = true;
|
|
331
|
+
Object.assign(frame.style, {
|
|
332
|
+
width: '100%',
|
|
333
|
+
aspectRatio: embedAspectRatio,
|
|
334
|
+
minHeight: '200px',
|
|
335
|
+
border: '0',
|
|
336
|
+
});
|
|
337
|
+
container.replaceChildren(frame);
|
|
338
|
+
} else {
|
|
339
|
+
if (container.querySelector('button')) return;
|
|
340
|
+
const button = document.createElement('button');
|
|
341
|
+
button.type = 'button';
|
|
342
|
+
button.textContent = 'Open privacy settings to view this content';
|
|
343
|
+
button.onclick = openPreferences;
|
|
344
|
+
container.replaceChildren(button);
|
|
345
|
+
}
|
|
346
|
+
};
|
|
347
|
+
|
|
348
|
+
render();
|
|
349
|
+
const unsubscribe = kernel.subscribe(render);
|
|
350
|
+
return () => {
|
|
351
|
+
unsubscribe();
|
|
352
|
+
container.replaceChildren();
|
|
353
|
+
};
|
|
354
|
+
}
|
|
155
355
|
```
|
|
156
356
|
|
|
157
|
-
|
|
158
|
-
|
|
357
|
+
Keep a transcript, address or other useful alternative outside the embed.
|
|
358
|
+
`loading="lazy"` is a performance hint; the consent condition controls whether
|
|
359
|
+
the iframe exists at all. A denied category may be fixed by policy, so opening
|
|
360
|
+
preferences does not guarantee that the visitor can grant it.
|
|
361
|
+
|
|
362
|
+
## Gate existing iframe markup
|
|
363
|
+
|
|
364
|
+
For markup that uses `data-category` and `data-src`, the
|
|
365
|
+
`createIframeBlocker` module from `c15t/modules/iframe-blocker` promotes
|
|
366
|
+
`data-src` only when the category has consent and the URL resolves to HTTP or
|
|
367
|
+
HTTPS. Relative URLs resolve against the iframe document's base URL.
|
|
368
|
+
Malformed URLs and other schemes, including `javascript:`, `data:` and `blob:`,
|
|
369
|
+
remain in `data-src` without loading. Correct the URL and call
|
|
370
|
+
`processAllIframes()` to retry.
|
|
159
371
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
of mounting an iframe.
|
|
372
|
+
Keep the initial URL in `data-src`, without `src`, so the browser cannot load
|
|
373
|
+
it before the blocker runs. The module does not sanitize arbitrary HTML or
|
|
374
|
+
modify iframes without `data-category`.
|
|
164
375
|
|
|
165
|
-
|
|
166
|
-
`YouTubeEmbed` forwards the iframe's native `onError` when a browser emits it,
|
|
167
|
-
but player-level errors require the YouTube IFrame Player API and are outside
|
|
168
|
-
this iframe-only component.
|
|
376
|
+
## Customize the player and placeholder
|
|
169
377
|
|
|
170
|
-
|
|
378
|
+
Set playback options in the iframe URL. For example, append `start=36` to begin
|
|
379
|
+
36 seconds into the video. Use supported
|
|
380
|
+
[YouTube player parameters](https://developers.google.com/youtube/player_parameters)
|
|
381
|
+
and reserve at least a 200-by-200-pixel player area.
|
|
171
382
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
5. Revoke consent and confirm the iframe is removed.
|
|
178
|
-
6. Confirm the default 16:9 frame is borderless and reserves the same space at
|
|
179
|
-
mobile and desktop widths.
|
|
383
|
+
React and Svelte `Frame` components provide a blocked-state placeholder and
|
|
384
|
+
consent action. Their `placeholder` APIs differ: React takes a node, Svelte
|
|
385
|
+
takes a snippet. Keep a clear way to reopen preferences when customizing them.
|
|
386
|
+
The Vue and browser examples show that action explicitly. `loading="lazy"` delays an
|
|
387
|
+
allowed iframe for performance; it does not implement consent gating.
|
|
180
388
|
|
|
181
|
-
|
|
182
|
-
for the supported query parameters.
|
|
389
|
+
## Consent behavior
|
|
183
390
|
|
|
184
|
-
|
|
391
|
+
Each example keeps the iframe absent until the category is allowed. After
|
|
392
|
+
permission, the iframe can load. Revoking permission removes the iframe and its
|
|
393
|
+
embedded document. Requests already sent cannot be recalled.
|
|
185
394
|
|
|
186
|
-
|
|
395
|
+
The `youtube-nocookie.com` hostname does not replace the consent boundary. Avoid
|
|
396
|
+
loading a remote thumbnail, preconnecting to YouTube, or installing the IFrame
|
|
397
|
+
Player API separately if you require no YouTube request before permission.
|
|
398
|
+
This recipe embeds a video; it does not load the JavaScript Player API.
|
|
187
399
|
|
|
188
|
-
|
|
189
|
-
|:--|:--|
|
|
190
|
-
|Type Name|\`YouTubeEmbedProps\`|
|
|
191
|
-
|Source Path|\`./packages/react/src/components/integrations/youtube-embed.tsx\`|
|
|
400
|
+
## Verify the integration
|
|
192
401
|
|
|
193
|
-
|
|
402
|
+
In a fresh opt-in session, confirm no iframe or YouTube request exists. Grant
|
|
403
|
+
marketing permission and play the video, then revoke it and confirm the iframe
|
|
404
|
+
is removed. Test a narrow viewport and keyboard access to the placeholder and
|
|
405
|
+
player. See [consent verification](../guides/verify-consent.md).
|