@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
|
@@ -1,33 +1,22 @@
|
|
|
1
1
|
---
|
|
2
|
-
title: Theme tokens
|
|
3
|
-
description:
|
|
4
|
-
|
|
2
|
+
title: Theme tokens
|
|
3
|
+
description: Change c15t's colors, type, radius, spacing, shadows and motion
|
|
4
|
+
with theme tokens, and see every --c15t-* variable with its default.
|
|
5
5
|
group: customization
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
##
|
|
8
|
+
## Where tokens come from
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
components and utilities directives in your Tailwind entry:
|
|
21
|
-
|
|
22
|
-
```css
|
|
23
|
-
@tailwind base;
|
|
24
|
-
@tailwind components;
|
|
25
|
-
@import 'c15t/react/styles.tw3.css';
|
|
26
|
-
@tailwind utilities;
|
|
27
|
-
```
|
|
28
|
-
|
|
29
|
-
Do not load both c15t stylesheet variants. For Tailwind 4 or unlayered CSS,
|
|
30
|
-
inspect layer order before reaching for `!important`.
|
|
10
|
+
c15t's components take their colors from `--c15t-*` CSS variables, and most
|
|
11
|
+
of their fonts, radii, spacing, shadows and motion too. c15t's stylesheet sets
|
|
12
|
+
the defaults. You change them with a theme object or with CSS, and every part
|
|
13
|
+
that reads a token changes with it. Some values are still fixed for one
|
|
14
|
+
component, such as button padding, the radius of the "Secured by" tag and
|
|
15
|
+
several font sizes and weights, so a token change does not move them. Restyle
|
|
16
|
+
those parts through [slots](./slots.md) or your own CSS.
|
|
17
|
+
[Stylesheets and CSS layers](./stylesheets.md)
|
|
18
|
+
covers which stylesheet to load, and [dark mode](./dark-mode.md)
|
|
19
|
+
covers the dark set of tokens.
|
|
31
20
|
|
|
32
21
|
## Set semantic values together
|
|
33
22
|
|
|
@@ -39,18 +28,116 @@ readable in each state.
|
|
|
39
28
|
import { defineTheme } from '@c15t/ui/theme';
|
|
40
29
|
|
|
41
30
|
export const theme = defineTheme({
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
31
|
+
colors: { primary: '#2f6f4e' },
|
|
32
|
+
radius: { lg: '4px' },
|
|
33
|
+
consentActions: {
|
|
34
|
+
primary: { variant: 'primary', mode: 'filled' },
|
|
35
|
+
dismiss: { variant: 'neutral', mode: 'stroke' },
|
|
36
|
+
},
|
|
48
37
|
});
|
|
49
38
|
```
|
|
50
39
|
|
|
51
|
-
Install `@c15t/ui` if importing its theme helper directly.
|
|
52
|
-
|
|
53
|
-
|
|
40
|
+
Install `@c15t/ui` if importing its theme helper directly. Where the theme
|
|
41
|
+
goes depends on the framework:
|
|
42
|
+
|
|
43
|
+
| Framework | Tokens | `consentActions` |
|
|
44
|
+
| ------------------------------ | --------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------- |
|
|
45
|
+
| Next.js, TanStack Start, React | `<ConsentTheme theme={theme} />`, rendered on the server where the app has one | `theme` in the provider options |
|
|
46
|
+
| Nuxt, Vue | `theme` or `tokens` in the module or plugin options | Not available |
|
|
47
|
+
| Astro | `theme` in the integration options | The same `theme` |
|
|
48
|
+
| Svelte, SvelteKit | `generateThemeCSS(theme)` from `@c15t/ui/theme` on the server, in a `<style>` element, or `--c15t-*` variables in your stylesheet | `theme` on `ConsentManagerProvider` |
|
|
49
|
+
| HTML, JavaScript | `ui.theme` | Not available |
|
|
50
|
+
|
|
51
|
+
React and Svelte providers do not turn tokens in their `theme` option into
|
|
52
|
+
CSS, and warn in development when a theme holds tokens but the page has no
|
|
53
|
+
`<style id="c15t-theme">`. Your framework's customize page shows the full
|
|
54
|
+
setup.
|
|
55
|
+
|
|
56
|
+
`consentActions` selects styling by action role. A per-action entry overrides
|
|
57
|
+
`primary`, which overrides `default`.
|
|
58
|
+
|
|
59
|
+
## Combine a generated theme with your own CSS
|
|
60
|
+
|
|
61
|
+
`generateThemeCSS` writes its variables on `:root:root` and
|
|
62
|
+
`.c15t-theme-root.c15t-theme-root`, one step more specific than the defaults
|
|
63
|
+
in `styles.css`. The theme therefore overrides the defaults whether its
|
|
64
|
+
`<style>` element comes before or after the stylesheet. The same output backs
|
|
65
|
+
`ConsentTheme` in React, Next.js and TanStack Start, Astro's `theme` option and
|
|
66
|
+
the script tag's `ui.theme`.
|
|
67
|
+
|
|
68
|
+
A `--c15t-*` variable you set on plain `:root` in your own CSS loses to a
|
|
69
|
+
generated theme that sets the same variable, even when your rule loads later.
|
|
70
|
+
Put the value in the theme, or raise your selector:
|
|
71
|
+
|
|
72
|
+
```css
|
|
73
|
+
:root:root {
|
|
74
|
+
--c15t-primary: #2f6f4e;
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Scoped rules such as `[data-prompt] { --c15t-primary: ... }` set the variable
|
|
79
|
+
on the banner element itself, so they still apply inside it.
|
|
80
|
+
|
|
81
|
+
## Every token
|
|
82
|
+
|
|
83
|
+
Every framework uses the same tokens. A theme object, such as `ConsentTheme`'s
|
|
84
|
+
`theme`, Astro's and Vue's `theme` or the script tag's `ui.theme`, takes the
|
|
85
|
+
theme key, such as `radius.lg`. Vue and Nuxt `tokens` take the CSS variable
|
|
86
|
+
name without the leading `--`, such as `c15t-radius-lg`. A stylesheet sets the
|
|
87
|
+
CSS variable itself. Your framework's customize page shows where each one goes.
|
|
88
|
+
[Motion and animation](./motion.md) explains the duration and
|
|
89
|
+
easing tokens.
|
|
90
|
+
|
|
91
|
+
| CSS variable | Theme key | Default |
|
|
92
|
+
| ----------------------------- | -------------------------------- | ----------------------------------------------- |
|
|
93
|
+
| `--c15t-primary` | `colors.primary` | `hsl(228, 100%, 60%)` |
|
|
94
|
+
| `--c15t-primary-hover` | `colors.primaryHover` | `hsl(228, 100%, 55%)` |
|
|
95
|
+
| `--c15t-surface` | `colors.surface` | `hsl(0, 0%, 100%)` |
|
|
96
|
+
| `--c15t-surface-hover` | `colors.surfaceHover` | `hsl(0, 0%, 98%)` |
|
|
97
|
+
| `--c15t-border` | `colors.border` | `hsl(0, 0%, 90%)` |
|
|
98
|
+
| `--c15t-border-hover` | `colors.borderHover` | `hsl(0, 0%, 85%)` |
|
|
99
|
+
| `--c15t-text` | `colors.text` | `hsl(0, 0%, 10%)` |
|
|
100
|
+
| `--c15t-text-muted` | `colors.textMuted` | `hsl(0, 0%, 40%)` |
|
|
101
|
+
| `--c15t-text-on-primary` | `colors.textOnPrimary` | auto-derived from `colors.primary` when omitted |
|
|
102
|
+
| `--c15t-overlay` | `colors.overlay` | `hsla(0, 0%, 0%, 0.5)` |
|
|
103
|
+
| `--c15t-switch-track` | `colors.switchTrack` | `hsl(0, 0%, 85%)` |
|
|
104
|
+
| `--c15t-switch-track-active` | `colors.switchTrackActive` | `hsl(228, 100%, 60%)` |
|
|
105
|
+
| `--c15t-switch-thumb` | `colors.switchThumb` | `hsl(0, 0%, 100%)` |
|
|
106
|
+
| `--c15t-font-family` | `typography.fontFamily` | `system-ui, -apple-system, sans-serif` |
|
|
107
|
+
| `--c15t-font-size-sm` | `typography.fontSize.sm` | `0.875rem` |
|
|
108
|
+
| `--c15t-font-size-base` | `typography.fontSize.base` | `1rem` |
|
|
109
|
+
| `--c15t-font-size-lg` | `typography.fontSize.lg` | `1.125rem` |
|
|
110
|
+
| `--c15t-font-weight-normal` | `typography.fontWeight.normal` | `400` |
|
|
111
|
+
| `--c15t-font-weight-medium` | `typography.fontWeight.medium` | `500` |
|
|
112
|
+
| `--c15t-font-weight-semibold` | `typography.fontWeight.semibold` | `600` |
|
|
113
|
+
| `--c15t-line-height-tight` | `typography.lineHeight.tight` | `1.25` |
|
|
114
|
+
| `--c15t-line-height-normal` | `typography.lineHeight.normal` | `1.5` |
|
|
115
|
+
| `--c15t-line-height-relaxed` | `typography.lineHeight.relaxed` | `1.75` |
|
|
116
|
+
| `--c15t-space-xs` | `spacing.xs` | `0.25rem` |
|
|
117
|
+
| `--c15t-space-sm` | `spacing.sm` | `0.5rem` |
|
|
118
|
+
| `--c15t-space-md` | `spacing.md` | `1rem` |
|
|
119
|
+
| `--c15t-space-lg` | `spacing.lg` | `1.5rem` |
|
|
120
|
+
| `--c15t-space-xl` | `spacing.xl` | `2rem` |
|
|
121
|
+
| `--c15t-radius-sm` | `radius.sm` | `0.25rem` |
|
|
122
|
+
| `--c15t-radius-md` | `radius.md` | `0.5rem` |
|
|
123
|
+
| `--c15t-radius-lg` | `radius.lg` | `0.75rem` |
|
|
124
|
+
| `--c15t-radius-full` | `radius.full` | `9999px` |
|
|
125
|
+
| `--c15t-shadow-sm` | `shadows.sm` | `0 1px 2px hsla(0, 0%, 0%, 0.05)` |
|
|
126
|
+
| `--c15t-shadow-md` | `shadows.md` | `0 4px 12px hsla(0, 0%, 0%, 0.08)` |
|
|
127
|
+
| `--c15t-shadow-lg` | `shadows.lg` | `0 8px 24px hsla(0, 0%, 0%, 0.12)` |
|
|
128
|
+
| `--c15t-duration-fast` | `motion.duration.fast` | `80ms` |
|
|
129
|
+
| `--c15t-duration-normal` | `motion.duration.normal` | `150ms` |
|
|
130
|
+
| `--c15t-duration-slow` | `motion.duration.slow` | `200ms` |
|
|
131
|
+
| `--c15t-easing` | `motion.easing` | `cubic-bezier(0.4, 0, 0.2, 1)` |
|
|
132
|
+
| `--c15t-easing-out` | `motion.easingOut` | `cubic-bezier(0.215, 0.61, 0.355, 1)` |
|
|
133
|
+
| `--c15t-easing-in-out` | `motion.easingInOut` | `cubic-bezier(0.645, 0.045, 0.355, 1)` |
|
|
134
|
+
| `--c15t-easing-spring` | `motion.easingSpring` | `cubic-bezier(0.34, 1.56, 0.64, 1)` |
|
|
135
|
+
|
|
136
|
+
The radius tokens round different parts. `radius.lg` rounds the banner card,
|
|
137
|
+
the preference dialog, `ConsentGate` placeholders and the floating trigger.
|
|
138
|
+
`radius.md` rounds buttons, accordions, tabs and the vendor list.
|
|
139
|
+
`radius.sm` rounds small parts inside the banner and dialog. To give the banner
|
|
140
|
+
and its buttons the same 4px corners, set both `lg` and `md`.
|
|
54
141
|
|
|
55
142
|
## Target a prompt with CSS
|
|
56
143
|
|
|
@@ -63,8 +150,9 @@ entry overrides `primary`, which overrides `default`.
|
|
|
63
150
|
```
|
|
64
151
|
|
|
65
152
|
Use attributes exposed by the rendered component, not guessed class names.
|
|
66
|
-
|
|
67
|
-
|
|
153
|
+
The script tag's banner has no `data-prompt` or `data-model`. Test the prompt
|
|
154
|
+
and the preferences dialog separately because tokens scoped to one prompt do
|
|
155
|
+
not automatically reach a portaled dialog.
|
|
68
156
|
|
|
69
157
|
| Size variable | Default | Target |
|
|
70
158
|
| ----------------------------------- | ------- | ------------- |
|
|
@@ -72,5 +160,47 @@ one prompt do not automatically reach a portaled dialog.
|
|
|
72
160
|
| `--consent-banner-widget-max-width` | `20rem` | Widget |
|
|
73
161
|
| `--consent-banner-wall-max-width` | `30rem` | Choice wall |
|
|
74
162
|
|
|
163
|
+
The banner footer lays out its actions by the card's width, not the
|
|
164
|
+
viewport's. With the default `compact` profile, a card narrower than 22rem
|
|
165
|
+
puts Reject and Accept on one row and Customize on a full-width row below
|
|
166
|
+
them, on any screen size.
|
|
167
|
+
|
|
75
168
|
Test long translations and small screens after changing width or typography.
|
|
76
169
|
A compact banner must still fit the required actions.
|
|
170
|
+
|
|
171
|
+
## Restyle the "Secured by" tag
|
|
172
|
+
|
|
173
|
+
The tag sits on the edge of the banner and dialog cards and uses the primary
|
|
174
|
+
color by default. Set these variables on `:root`, or on an element that
|
|
175
|
+
contains the tag. The dialog renders in a portal, so a variable set on the
|
|
176
|
+
banner does not reach the dialog's tag.
|
|
177
|
+
|
|
178
|
+
| Variable | Default | Target |
|
|
179
|
+
| -------------------------------------------- | --------------------------------------- | -------------------------------------- |
|
|
180
|
+
| `--consent-branding-tag-background-color` | `var(--c15t-primary)` | Tag background |
|
|
181
|
+
| `--consent-branding-tag-border-color` | `--c15t-primary` mixed 14% toward black | Tag border |
|
|
182
|
+
| `--consent-branding-tag-text-color` | `var(--c15t-text-on-primary, #fff)` | "Secured by" and the wordmark |
|
|
183
|
+
| `--consent-branding-tag-mark-color` | The text color | c15t mark or inth logo |
|
|
184
|
+
| `--consent-branding-tag-shadow` | Inset highlight and a 1px drop shadow | Tag shadow |
|
|
185
|
+
| `--consent-branding-tag-attached-edge-width` | `0px` | Border on the edge that meets the card |
|
|
186
|
+
|
|
187
|
+
The stylesheet does not declare these variables. Each default resolves on the
|
|
188
|
+
tag, so a `--c15t-primary` you scope to a banner still colors the tag.
|
|
189
|
+
|
|
190
|
+
This makes the tag look like a tab of the card:
|
|
191
|
+
|
|
192
|
+
```css
|
|
193
|
+
:root {
|
|
194
|
+
--consent-branding-tag-background-color: var(--c15t-surface);
|
|
195
|
+
--consent-branding-tag-border-color: var(--c15t-border);
|
|
196
|
+
--consent-branding-tag-text-color: var(--c15t-text-muted);
|
|
197
|
+
--consent-branding-tag-mark-color: var(--c15t-primary);
|
|
198
|
+
--consent-branding-tag-shadow: none;
|
|
199
|
+
}
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
The edge that meets the card has no border by default. Above the banner the
|
|
203
|
+
tag overlaps the card's top border by 1px and covers it. Below the dialog the
|
|
204
|
+
tag starts under the card's bottom border. Set
|
|
205
|
+
`--consent-branding-tag-attached-edge-width: 1px` to draw that edge. It is
|
|
206
|
+
drawn over the card's border, so the two borders do not stack.
|
|
@@ -27,9 +27,25 @@ i18n: {
|
|
|
27
27
|
|
|
28
28
|
Supply the same message keys in each supported locale. A one-off component prop
|
|
29
29
|
such as `dismissButtonText` is useful for one banner; use translations for a
|
|
30
|
-
site-wide change. Astro's serializable integration options
|
|
31
|
-
|
|
32
|
-
|
|
30
|
+
site-wide change. Astro's serializable integration options have their own
|
|
31
|
+
types, so verify that shape before copying a React object. The Vue plugin and
|
|
32
|
+
Nuxt module have no `i18n` option; their copy comes from the backend.
|
|
33
|
+
|
|
34
|
+
## Combine messages with backend copy
|
|
35
|
+
|
|
36
|
+
With a backend or a manifest, the copy it sends for the visitor's language is
|
|
37
|
+
the base. Your `i18n.messages` for that language replace it key by key, and
|
|
38
|
+
keys you leave out keep the backend's wording, including copy edited in your
|
|
39
|
+
Inth project. c15t looks up messages for the exact language first, then for
|
|
40
|
+
its primary language, so `de-AT` uses your `de` messages when there is no
|
|
41
|
+
`de-AT` entry. Messages for other languages are not applied.
|
|
42
|
+
|
|
43
|
+
A key only overrides the backend when your text differs from c15t's built-in
|
|
44
|
+
wording for that language. So passing the stock bundles from
|
|
45
|
+
`@c15t/translations/all` to enable languages keeps backend edits visible,
|
|
46
|
+
while a key you actually reworded stays pinned in code. Core bundles only
|
|
47
|
+
English; for other languages, import `@c15t/translations/all` so c15t can
|
|
48
|
+
recognize its stock wording.
|
|
33
49
|
|
|
34
50
|
## Write labels that describe the action
|
|
35
51
|
|
|
@@ -41,6 +57,48 @@ The notice acknowledgement uses `common.acknowledge`, with `common.dismiss` as
|
|
|
41
57
|
a fallback for older translation bundles. Keep the displayed label and the
|
|
42
58
|
command's effect aligned.
|
|
43
59
|
|
|
60
|
+
## Translate the ConsentGate placeholder
|
|
61
|
+
|
|
62
|
+
The `ConsentGate` placeholder reads the `consentGate` section:
|
|
63
|
+
|
|
64
|
+
| Key | Where it shows |
|
|
65
|
+
| --------------------------- | ------------------------------------------------------------------------------------------------------------------- |
|
|
66
|
+
| `consentGate.title` | The placeholder text. `{category}` is replaced by the category's translated title. |
|
|
67
|
+
| `consentGate.actionButton` | The button that opens preferences, with the same `{category}` replacement. |
|
|
68
|
+
| `consentGate.policyBlocked` | React and Vue show it in place of the title, with no button, when a strict policy leaves the category out of scope. |
|
|
69
|
+
|
|
70
|
+
Earlier versions called this section `frame`. Copy under `frame` in
|
|
71
|
+
`i18n.messages`, custom translations or an older backend's `/init` response
|
|
72
|
+
still applies. c15t reads it as `consentGate`, a key set under `consentGate`
|
|
73
|
+
wins over the same key under `frame`, and c15t logs a warning once outside
|
|
74
|
+
production. Rename the section to `consentGate` to remove the warning.
|
|
75
|
+
|
|
76
|
+
## Show right-to-left languages
|
|
77
|
+
|
|
78
|
+
c15t sets `dir="rtl"` on its surfaces when the resolved language is Arabic,
|
|
79
|
+
Hebrew, Persian, Urdu, Pashto, Sindhi, Kurdish or Dhivehi. It matches the
|
|
80
|
+
primary language, so `ar-EG` counts as Arabic. Hebrew is the only
|
|
81
|
+
right-to-left translation c15t ships. For the others, supply your own
|
|
82
|
+
messages or the copy from your Inth project.
|
|
83
|
+
|
|
84
|
+
What follows the direction:
|
|
85
|
+
|
|
86
|
+
* Text, headings and the button row in the banner, the preference dialog and
|
|
87
|
+
the preference widget flow right to left.
|
|
88
|
+
* A floating or widget banner in its default corner moves to the mirrored
|
|
89
|
+
corner, so `bottom-left` becomes `bottom-right`. A `position` you set
|
|
90
|
+
yourself stays where you put it.
|
|
91
|
+
* The HTML script tag, `@c15t/browser` and Astro's banner also set `lang` on
|
|
92
|
+
their surfaces. React, Vue and Svelte set `dir` only, so set `lang` on
|
|
93
|
+
`<html>` yourself.
|
|
94
|
+
|
|
95
|
+
What does not follow yet:
|
|
96
|
+
|
|
97
|
+
* The IAB TCF dialog aligns several labels, indents and borders to the left.
|
|
98
|
+
* The floating `ConsentDialogTrigger` keeps the corner you give it.
|
|
99
|
+
|
|
100
|
+
Test right-to-left pages with a real translation before you ship them.
|
|
101
|
+
|
|
44
102
|
## Test more than English
|
|
45
103
|
|
|
46
104
|
Try the longest labels you support at a narrow width, with browser zoom and
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Embeds
|
|
3
|
+
description: Gate YouTube videos, maps, social posts and other iframes on an
|
|
4
|
+
Astro site so they load only after the visitor allows their consent category,
|
|
5
|
+
with a custom element or the c15t iframe blocker.
|
|
6
|
+
group: frameworks
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Why an embed needs gating
|
|
10
|
+
|
|
11
|
+
An iframe sends requests to its host as soon as it is in the page with a
|
|
12
|
+
`src`, before any script can stop it. `ConsentBanner` does not block iframes
|
|
13
|
+
you already have. Render an embed only while its category is allowed, and
|
|
14
|
+
remove it when the visitor withdraws permission.
|
|
15
|
+
|
|
16
|
+
Astro has no consent gate component. Use one of these:
|
|
17
|
+
|
|
18
|
+
| Approach | Use it when |
|
|
19
|
+
| ------------------------------------------------------- | ---------------------------------------------------------- |
|
|
20
|
+
| A custom element that renders the iframe | You want a placeholder with a button in place of the embed |
|
|
21
|
+
| The iframe blocker, with `data-category` and `data-src` | You have iframe markup to gate as it is |
|
|
22
|
+
|
|
23
|
+
## Gate an embed with a custom element
|
|
24
|
+
|
|
25
|
+
This component shows a placeholder with a preferences button until
|
|
26
|
+
measurement is allowed. It adds the iframe once the visitor allows
|
|
27
|
+
measurement, and removes it when permission is withdrawn. It also keeps the
|
|
28
|
+
iframe out while the visitor has switched YouTube off in
|
|
29
|
+
[vendor consent](https://c15t.com/docs/frameworks/astro/vendor-consent).
|
|
30
|
+
|
|
31
|
+
The component reads `client.isVendorAllowed('youtube')`, so declare `youtube`
|
|
32
|
+
in the `vendors` option of `c15t()` with `category: 'measurement'`. An
|
|
33
|
+
undeclared vendor reads as not allowed, and the video never loads:
|
|
34
|
+
|
|
35
|
+
```astro title="src/components/consent-video.astro"
|
|
36
|
+
---
|
|
37
|
+
import ConsentDialogTrigger from 'c15t/astro/components/consent-dialog-trigger.astro';
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
<consent-video>
|
|
41
|
+
<div data-video>
|
|
42
|
+
<p>Allow measurement to load this YouTube video.</p>
|
|
43
|
+
</div>
|
|
44
|
+
<ConsentDialogTrigger>Open privacy settings</ConsentDialogTrigger>
|
|
45
|
+
</consent-video>
|
|
46
|
+
|
|
47
|
+
<script>
|
|
48
|
+
import { getConsentClient } from 'c15t/astro/client';
|
|
49
|
+
|
|
50
|
+
class ConsentVideo extends HTMLElement {
|
|
51
|
+
dispose?: () => void;
|
|
52
|
+
|
|
53
|
+
connect = () => {
|
|
54
|
+
const client = getConsentClient();
|
|
55
|
+
const container = this.querySelector('[data-video]');
|
|
56
|
+
if (this.dispose || !client || !container) {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
const render = () => {
|
|
60
|
+
// Measurement is allowed and the visitor has not switched YouTube
|
|
61
|
+
// off. An undeclared vendor is never allowed, so declare youtube.
|
|
62
|
+
if (!client.isVendorAllowed('youtube')) {
|
|
63
|
+
container.textContent =
|
|
64
|
+
'Allow measurement to load this YouTube video. No video request is sent before permission.';
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
if (container.querySelector('iframe')) {
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
const frame = document.createElement('iframe');
|
|
71
|
+
frame.src = 'https://www.youtube-nocookie.com/embed/czTksCF6X8Y';
|
|
72
|
+
frame.title = 'YouTube video';
|
|
73
|
+
frame.allowFullscreen = true;
|
|
74
|
+
container.replaceChildren(frame);
|
|
75
|
+
};
|
|
76
|
+
render();
|
|
77
|
+
this.dispose = client.subscribe(render);
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
connectedCallback() {
|
|
81
|
+
// c15t boots from a module script, which can run after this one.
|
|
82
|
+
document.addEventListener('DOMContentLoaded', this.connect, {
|
|
83
|
+
once: true,
|
|
84
|
+
});
|
|
85
|
+
this.connect();
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
disconnectedCallback() {
|
|
89
|
+
document.removeEventListener('DOMContentLoaded', this.connect);
|
|
90
|
+
this.dispose?.();
|
|
91
|
+
this.dispose = undefined;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (!customElements.get('consent-video')) {
|
|
96
|
+
customElements.define('consent-video', ConsentVideo);
|
|
97
|
+
}
|
|
98
|
+
</script>
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Use it like any Astro component. How it works:
|
|
102
|
+
|
|
103
|
+
* The iframe does not exist in the server HTML, so nothing loads before
|
|
104
|
+
consent, even before the consent runtime starts.
|
|
105
|
+
* `client.subscribe(render)` renders again on every consent change.
|
|
106
|
+
* `connectedCallback` runs again when `ClientRouter` swaps in a page that
|
|
107
|
+
contains the element, so the embed works across navigation.
|
|
108
|
+
* The first `connect()` can run before c15t has started, so the element tries
|
|
109
|
+
again on `DOMContentLoaded`.
|
|
110
|
+
|
|
111
|
+
Change the category, the iframe `src` and the placeholder text for other
|
|
112
|
+
embeds. The [YouTube](../../integrations/youtube.md) and
|
|
113
|
+
[Google Maps](../../integrations/google-maps.md) guides use the same pattern with
|
|
114
|
+
a reusable browser helper. [Integrations](../../integrations/overview.md) lists
|
|
115
|
+
the other vendors.
|
|
116
|
+
|
|
117
|
+
## Gate existing iframe markup
|
|
118
|
+
|
|
119
|
+
The consent runtime includes an iframe blocker, on by default. Mark an iframe
|
|
120
|
+
with a category and move its URL from `src` to `data-src`:
|
|
121
|
+
|
|
122
|
+
```html title="src/pages/contact.astro (partial)"
|
|
123
|
+
<iframe
|
|
124
|
+
data-category="marketing"
|
|
125
|
+
data-src="https://www.google.com/maps/embed?pb=..."
|
|
126
|
+
title="Office location"
|
|
127
|
+
></iframe>
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
When the category is allowed, the blocker copies `data-src` to `src`. When it
|
|
131
|
+
is withdrawn, the blocker removes `src` again. `data-vendor` gates the iframe
|
|
132
|
+
on one vendor as well. See [vendor consent](https://c15t.com/docs/frameworks/astro/vendor-consent).
|
|
133
|
+
|
|
134
|
+
Always use `data-src`, never `src`, for a gated iframe. The browser starts
|
|
135
|
+
loading a `src` from the HTML before the blocker runs, so an iframe with `src`
|
|
136
|
+
in the markup loads before consent.
|
|
137
|
+
|
|
138
|
+
The blocker watches the whole document, so it also gates iframes on pages you
|
|
139
|
+
reach with `ClientRouter`, which replaces `<body>` on each navigation.
|
|
140
|
+
|
|
141
|
+
## Gate an embed on the server
|
|
142
|
+
|
|
143
|
+
On a server-rendered page, `Astro.locals.c15t.snapshot.effectivePermissions`
|
|
144
|
+
tells you whether the visitor had allowed the category when the request
|
|
145
|
+
arrived. Rendering the iframe on the server from it works for returning
|
|
146
|
+
visitors. A visitor who allows the category on the page sees the embed only
|
|
147
|
+
after the next navigation, so pair it with the custom element, or use the
|
|
148
|
+
custom element alone. See [Server API](https://c15t.com/docs/frameworks/astro/server).
|
|
149
|
+
|
|
150
|
+
## Check the embeds
|
|
151
|
+
|
|
152
|
+
1. Open the page in a private window with DevTools Network open. There is no
|
|
153
|
+
request to the embed's host, and no iframe from it in the Elements panel.
|
|
154
|
+
2. Allow the embed's category from **Cookie preferences**. The iframe appears
|
|
155
|
+
and loads.
|
|
156
|
+
3. Reload. The iframe loads again without a new choice.
|
|
157
|
+
4. Withdraw the category and save. The page reloads, and the embed's host
|
|
158
|
+
gets no request.
|
|
159
|
+
|
|
160
|
+
See [Verify consent](../../guides/verify-consent.md) for the full checklist.
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Network blocker
|
|
3
|
+
description: Block fetch and XMLHttpRequest calls to tracking hosts on an Astro
|
|
4
|
+
site until the visitor allows their consent category, with c15t's network
|
|
5
|
+
blocker rules and an onRequestBlocked handler.
|
|
6
|
+
group: frameworks
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## What the network blocker does
|
|
10
|
+
|
|
11
|
+
`networkBlocker` stops `fetch` and `XMLHttpRequest` calls that match a rule
|
|
12
|
+
until the visitor allows the rule's category. A blocked `fetch` resolves to a
|
|
13
|
+
`451` response, and nothing is sent. It covers requests that code already on
|
|
14
|
+
the page makes, such as an SDK you load yourself or a tag manager's own calls.
|
|
15
|
+
|
|
16
|
+
It does not stop `navigator.sendBeacon`, WebSockets, image pixels, `<script>`
|
|
17
|
+
tags or iframes. Load scripts through c15t and gate iframes as
|
|
18
|
+
[Scripts](./scripts.md) and
|
|
19
|
+
[Embeds](./embeds.md) describe.
|
|
20
|
+
|
|
21
|
+
## Add rules
|
|
22
|
+
|
|
23
|
+
Rules are plain data, so they can go in the integration options:
|
|
24
|
+
|
|
25
|
+
```js title="astro.config.mjs (partial)"
|
|
26
|
+
c15t({
|
|
27
|
+
mode: hosted({ url: 'https://your-project.inth.app' }),
|
|
28
|
+
networkBlocker: {
|
|
29
|
+
rules: [
|
|
30
|
+
{ category: 'measurement', domain: 'google-analytics.com' },
|
|
31
|
+
{
|
|
32
|
+
category: 'marketing',
|
|
33
|
+
domain: 'ads.example.com',
|
|
34
|
+
pathIncludes: '/collect',
|
|
35
|
+
methods: ['POST'],
|
|
36
|
+
},
|
|
37
|
+
],
|
|
38
|
+
},
|
|
39
|
+
});
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
| Rule field | Effect |
|
|
43
|
+
| -------------- | ----------------------------------------------------------------- |
|
|
44
|
+
| `category` | The category that must be allowed for the request to go through |
|
|
45
|
+
| `domain` | The host to match. It also matches every subdomain |
|
|
46
|
+
| `pathIncludes` | Matches only URLs whose path contains this text |
|
|
47
|
+
| `methods` | Matches only these HTTP methods |
|
|
48
|
+
| `vendor` | Also requires this vendor to be allowed, for vendor-level consent |
|
|
49
|
+
|
|
50
|
+
| Option | Default | Effect |
|
|
51
|
+
| -------------------- | -------- | ------------------------------------------------------------------- |
|
|
52
|
+
| `rules` | Required | The rules to apply |
|
|
53
|
+
| `enabled` | `true` | Set `false` to keep the rules but stop blocking |
|
|
54
|
+
| `logBlockedRequests` | `true` | Logs each blocked request to the console. Set `false` to silence it |
|
|
55
|
+
|
|
56
|
+
The blocker starts with the consent runtime, from a module script. A request
|
|
57
|
+
made before that, such as from an inline script at the top of `<head>`, is not
|
|
58
|
+
blocked.
|
|
59
|
+
|
|
60
|
+
## Log or report blocked requests
|
|
61
|
+
|
|
62
|
+
`onRequestBlocked` is a function, so it cannot go in `astro.config.mjs`. Set
|
|
63
|
+
`networkBlocker` in the default export of your client entrypoint instead. It
|
|
64
|
+
replaces the integration's `networkBlocker` completely, so repeat the rules
|
|
65
|
+
there:
|
|
66
|
+
|
|
67
|
+
```ts title="src/consent-client.ts (partial)"
|
|
68
|
+
export default {
|
|
69
|
+
scripts,
|
|
70
|
+
networkBlocker: {
|
|
71
|
+
rules: [{ category: 'measurement', domain: 'google-analytics.com' }],
|
|
72
|
+
onRequestBlocked: ({ url, rule }) => {
|
|
73
|
+
console.info('Blocked until consent:', url, rule?.category);
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
} satisfies C15tClientOptionsExtension;
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Check the network blocker
|
|
80
|
+
|
|
81
|
+
1. Open the site in a private window with DevTools Network open, and trigger
|
|
82
|
+
the code that calls a blocked host. The request does not appear, and a
|
|
83
|
+
`fetch` receives a `451` response.
|
|
84
|
+
2. Allow the rule's category. The next request to that host goes through.
|
|
85
|
+
3. Withdraw the category and save. After the reload, requests to the host are
|
|
86
|
+
blocked again.
|