@c15t/react 3.0.0-alpha.9 → 3.0.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.
Files changed (147) hide show
  1. package/AGENTS.md +7 -9
  2. package/README.md +41 -70
  3. package/SKILL.md +1 -1
  4. package/dist/aggregate-components.js +1 -1
  5. package/dist/components/consent-gate/atoms.js +1 -1
  6. package/dist/components/consent-gate/frame.js +2 -0
  7. package/dist/components/consent-gate/index.js +1 -1
  8. package/dist/components/iab-panel/atoms/root.js +1 -1
  9. package/dist/components/iab-panel/iab-panel.js +1 -1
  10. package/dist/components/iab-prompt/atoms/root.js +1 -1
  11. package/dist/components/iab-prompt/iab-prompt.js +1 -1
  12. package/dist/components/panel/atoms/root.js +1 -1
  13. package/dist/components/panel-trigger/atoms/root.js +1 -1
  14. package/dist/components/panel-trigger/panel-trigger.js +1 -1
  15. package/dist/components/panel-trigger/toolbar-loader.js +2 -0
  16. package/dist/components/preferences/atoms/root.js +1 -1
  17. package/dist/components/prompt/atoms/early-tap-script.js +2 -0
  18. package/dist/components/prompt/atoms/root.js +1 -1
  19. package/dist/components/shared/primitives/button.js +1 -1
  20. package/dist/components/shared/surface-styles.js +1 -1
  21. package/dist/consent-gate.js +1 -1
  22. package/dist/context/index.js +1 -1
  23. package/dist/devtools.js +1 -1
  24. package/dist/iab/styles.tw3.css +2 -2
  25. package/dist/iab-dialog-deferred.js +2 -0
  26. package/dist/iab.js +1 -1
  27. package/dist/index-parts/consent-gate.js +1 -1
  28. package/dist/index-parts/core.js +1 -1
  29. package/dist/index-parts/frame.js +1 -0
  30. package/dist/index-parts/hosted.js +1 -0
  31. package/dist/index-parts/offline.js +1 -0
  32. package/dist/index-parts/presets.js +1 -0
  33. package/dist/index.js +1 -1
  34. package/dist/modes.js +1 -0
  35. package/dist/panel-link.js +1 -1
  36. package/dist/panel-trigger.js +1 -1
  37. package/dist/provider.js +1 -1
  38. package/dist/styles.tw3.css +2 -2
  39. package/dist/transports/hosted.js +1 -0
  40. package/dist/transports/manifest.js +1 -0
  41. package/dist/utils/deferred-module.js +1 -1
  42. package/dist/version.js +1 -1
  43. package/dist-types/chunk-warming.d.ts +7 -6
  44. package/dist-types/components/consent-gate/frame.d.ts +19 -0
  45. package/dist-types/components/consent-gate/index.d.ts +3 -19
  46. package/dist-types/components/iab-prompt/iab-prompt.d.ts +1 -1
  47. package/dist-types/components/panel/index.d.ts +1 -1
  48. package/dist-types/components/panel-trigger/toolbar-loader.d.ts +34 -0
  49. package/dist-types/components/preferences/index.d.ts +1 -1
  50. package/dist-types/components/prompt/atoms/early-tap-script.d.ts +17 -0
  51. package/dist-types/components/prompt/index.d.ts +1 -1
  52. package/dist-types/components/shared/surface-styles.d.ts +26 -2
  53. package/dist-types/consent-gate.d.ts +1 -1
  54. package/dist-types/context/index.d.ts +3 -3
  55. package/dist-types/context.d.ts +2 -0
  56. package/dist-types/gpp.d.ts +1 -1
  57. package/dist-types/iab-dialog-deferred.d.ts +36 -0
  58. package/dist-types/iab.d.ts +3 -2
  59. package/dist-types/index-parts/consent-gate.d.ts +1 -1
  60. package/dist-types/index-parts/core.d.ts +1 -2
  61. package/dist-types/index-parts/frame.d.ts +7 -0
  62. package/dist-types/index-parts/hosted.d.ts +7 -0
  63. package/dist-types/index-parts/offline.d.ts +7 -0
  64. package/dist-types/index-parts/presets.d.ts +7 -0
  65. package/dist-types/index.d.ts +5 -1
  66. package/dist-types/modes.d.ts +26 -0
  67. package/dist-types/panel-link.d.ts +1 -1
  68. package/dist-types/panel-trigger.d.ts +1 -1
  69. package/dist-types/provider.d.ts +3 -3
  70. package/dist-types/transports/hosted.d.ts +36 -0
  71. package/dist-types/transports/manifest.d.ts +48 -0
  72. package/dist-types/version.d.ts +1 -1
  73. package/docs/README.md +7 -9
  74. package/docs/concepts/choose-your-setup.md +45 -10
  75. package/docs/concepts/consent-categories.md +1 -1
  76. package/docs/concepts/consent-state.md +5 -2
  77. package/docs/concepts/data-fetching.md +100 -42
  78. package/docs/concepts/modes.md +281 -0
  79. package/docs/customization/dark-mode.md +1 -1
  80. package/docs/customization/motion.md +2 -2
  81. package/docs/customization/recipes.md +4 -4
  82. package/docs/customization/slots.md +25 -25
  83. package/docs/customization/stylesheets.md +12 -14
  84. package/docs/customization/tailwind.md +17 -11
  85. package/docs/customization/tokens.md +36 -7
  86. package/docs/frameworks/react/components/consent-banner.md +14 -10
  87. package/docs/frameworks/react/components/consent-dialog.md +14 -10
  88. package/docs/frameworks/react/components/consent-provider.md +51 -44
  89. package/docs/frameworks/react/components.md +14 -10
  90. package/docs/frameworks/react/content-security-policy.md +21 -12
  91. package/docs/frameworks/react/hooks.md +2 -2
  92. package/docs/frameworks/react/iab.md +20 -2
  93. package/docs/frameworks/react/quickstart.md +63 -97
  94. package/docs/frameworks/react/rendering.md +168 -94
  95. package/docs/frameworks/react/scripts.md +19 -28
  96. package/docs/frameworks/react/troubleshooting.md +57 -25
  97. package/docs/frameworks/react/upgrade-v3.md +110 -62
  98. package/docs/guides/banner-experiments.md +21 -6
  99. package/docs/guides/troubleshooting.md +89 -25
  100. package/docs/guides/verify-consent.md +23 -8
  101. package/docs/integrations/adobe-analytics.md +60 -64
  102. package/docs/integrations/ahrefs-analytics.md +58 -63
  103. package/docs/integrations/amplitude.md +60 -64
  104. package/docs/integrations/clearbit.md +59 -63
  105. package/docs/integrations/cloudflare-web-analytics.md +56 -60
  106. package/docs/integrations/cloudflare-zaraz.md +55 -57
  107. package/docs/integrations/crisp.md +64 -69
  108. package/docs/integrations/databuddy.md +62 -64
  109. package/docs/integrations/fathom-analytics.md +56 -60
  110. package/docs/integrations/front-chat.md +56 -60
  111. package/docs/integrations/google-maps.md +25 -13
  112. package/docs/integrations/google-tag-manager.md +153 -77
  113. package/docs/integrations/google-tag.md +134 -78
  114. package/docs/integrations/heap.md +56 -60
  115. package/docs/integrations/hightouch.md +55 -60
  116. package/docs/integrations/hotjar.md +60 -64
  117. package/docs/integrations/intercom.md +55 -60
  118. package/docs/integrations/klaviyo.md +65 -66
  119. package/docs/integrations/linkedin-insights.md +58 -63
  120. package/docs/integrations/logrocket.md +55 -60
  121. package/docs/integrations/matomo-analytics.md +58 -60
  122. package/docs/integrations/meta-pixel.md +56 -60
  123. package/docs/integrations/microsoft-clarity.md +57 -60
  124. package/docs/integrations/microsoft-uet.md +58 -61
  125. package/docs/integrations/mixpanel-analytics.md +73 -75
  126. package/docs/integrations/one-dollar-stats.md +53 -59
  127. package/docs/integrations/openai-pixel.md +58 -60
  128. package/docs/integrations/overview.md +59 -42
  129. package/docs/integrations/pinterest-tag.md +56 -60
  130. package/docs/integrations/pirsch.md +54 -60
  131. package/docs/integrations/plausible-analytics.md +55 -60
  132. package/docs/integrations/posthog.md +56 -60
  133. package/docs/integrations/promptwatch.md +57 -63
  134. package/docs/integrations/reddit-pixel.md +57 -60
  135. package/docs/integrations/rudderstack.md +65 -67
  136. package/docs/integrations/rybbit-analytics.md +70 -75
  137. package/docs/integrations/segment.md +56 -60
  138. package/docs/integrations/sentry.md +65 -60
  139. package/docs/integrations/snapchat-pixel.md +54 -60
  140. package/docs/integrations/tiktok-pixel.md +59 -63
  141. package/docs/integrations/umami-analytics.md +54 -60
  142. package/docs/integrations/vercel-analytics.md +53 -59
  143. package/docs/integrations/vercel-speed-insights.md +317 -0
  144. package/docs/integrations/x-pixel.md +57 -63
  145. package/docs/integrations/youtube.md +25 -13
  146. package/package.json +18 -8
  147. package/docs/upgrade-v3.md +0 -429
@@ -12,15 +12,15 @@ Each framework has one way to add classes, inline styles or attributes to a
12
12
  single part of a stock component. The keys differ between the two
13
13
  vocabularies, so a configuration from one does not work in the other.
14
14
 
15
- | Framework | Part API | Keys look like | Class key |
16
- | ----------------------- | ------------------------------------------------------------- | ------------------------- | ------------------------ |
17
- | Next.js, TanStack Start | `options.components` on `ConsentRoot` | `components.banner.card` | `className` |
18
- | React | `components` in the `ConsentProvider` options | `components.banner.card` | `className` |
19
- | Nuxt | `components` in the `c15t` options, or in `app/app.config.ts` | `components.banner.card` | `class` |
20
- | Vue | `components` in the `c15tVue` options | `components.banner.card` | `class` |
21
- | Astro | `theme.slots` in the `c15t()` integration options | `slots.consentBannerCard` | A string, or `className` |
22
- | Svelte, SvelteKit | `theme.slots` on `ConsentManagerProvider` | `slots.consentBannerCard` | A string, or `className` |
23
- | HTML, JavaScript | `ui.theme.slots` | `slots.consentBannerCard` | A string, or `className` |
15
+ | Framework | Part API | Keys look like | Class key |
16
+ | ----------------------- | ------------------------------------------------------------------------ | ------------------------- | ------------------------ |
17
+ | Next.js, TanStack Start | `options.components` in Next.js `c15t.config.ts`, or on `ConsentRoot` | `components.banner.card` | `className` |
18
+ | React | `components` in the `ConsentProvider` options | `components.banner.card` | `className` |
19
+ | Nuxt | `components` in the `c15t` options, or in `app/app.config.ts` | `components.banner.card` | `class` |
20
+ | Vue | `components` in the `c15tVue` options | `components.banner.card` | `class` |
21
+ | Astro | `theme.slots` in the `c15t()` integration options | `slots.consentBannerCard` | A string, or `className` |
22
+ | Svelte, SvelteKit | `theme.slots` on `ConsentProvider` (Svelte) or `ConsentRoot` (SvelteKit) | `slots.consentBannerCard` | A string, or `className` |
23
+ | HTML, JavaScript | `ui.theme.slots` | `slots.consentBannerCard` | A string, or `className` |
24
24
 
25
25
  React and Vue also read `theme.slots` from the theme in their options, and
26
26
  apply each slot to the matching `components` part, such as
@@ -65,22 +65,22 @@ Modules, vanilla-extract, StyleX and Emotion, and
65
65
 
66
66
  ## Banner parts
67
67
 
68
- | Part | `components` key | `theme.slots` key | `data-testid` |
69
- | ------------------------------------------- | ------------------------------------ | ---------------------------------- | --------------------- |
70
- | Positioning root, with the state attributes | `banner.root` | `consentBanner` | `consent-banner-root` |
71
- | Card sizing and branding container | `banner.cardShell` | None | |
72
- | Visible card | `banner.card` | `consentBannerCard` | `consent-banner-card` |
73
- | Title and description container | `banner.header` | `consentBannerHeader` | |
74
- | Title | `banner.title` | `consentBannerTitle` | |
75
- | Description | `description.banner` | `consentBannerDescription` | |
76
- | Action area | `banner.footer` | `consentBannerFooter` | |
77
- | Group of action groups | `banner.actions` | None | |
78
- | One group of equally prominent actions | `banner.actionGroup` | `consentBannerFooterSubGroup` | |
79
- | Group of right links | `banner.rights` | `consentBannerRights` | |
80
- | One right link | `banner.rightLink` | `consentBannerRightLink` | |
81
- | Backdrop of a blocking banner | `banner.overlay` | `consentBannerOverlay` | |
82
- | "Secured by" tag | `tag.banner` | `consentBannerTag` | |
83
- | Filled and outlined buttons | `button.primary`, `button.secondary` | `buttonPrimary`, `buttonSecondary` | |
68
+ | Part | `components` key | `theme.slots` key | `data-testid` |
69
+ | ------------------------------------------- | ------------------------------------ | ---------------------------------- | ------------------------- |
70
+ | Positioning root, with the state attributes | `banner.root` | `consentBanner` | `consent-banner-root` |
71
+ | Card sizing and branding container | `banner.cardShell` | None | |
72
+ | Visible card | `banner.card` | `consentBannerCard` | `consent-banner-card` |
73
+ | Title and description container | `banner.header` | `consentBannerHeader` | |
74
+ | Title | `banner.title` | `consentBannerTitle` | |
75
+ | Description | `description.banner` | `consentBannerDescription` | |
76
+ | Action area | `banner.footer` | `consentBannerFooter` | |
77
+ | Group of action groups | `banner.actions` | None | |
78
+ | One group of equally prominent actions | `banner.actionGroup` | `consentBannerFooterSubGroup` | |
79
+ | Group of right links | `banner.rights` | `consentBannerRights` | |
80
+ | One right link | `banner.rightLink` | `consentBannerRightLink` | |
81
+ | Backdrop of a blocking banner | `banner.overlay` | `consentBannerOverlay` | |
82
+ | "Secured by" tag | `tag.banner` | `consentBannerTag` | `consent-banner-branding` |
83
+ | Filled and outlined buttons | `button.primary`, `button.secondary` | `buttonPrimary`, `buttonSecondary` | |
84
84
 
85
85
  The button parts apply to every c15t button of that style, in the banner and
86
86
  the dialog. The script tag's banner has no rights group, so
@@ -104,20 +104,18 @@ need control over the file:
104
104
  cache performs better for your site's repeat visits. Compare cold and warm
105
105
  visits on your production host before choosing this option.
106
106
 
107
- | Framework | Option | Files to import |
108
- | ----------------- | -------------------------------------------- | -------------------------------------------------------------------------------------- |
109
- | Next.js | `options.styles` on `ConsentRoot` | `c15t/next/styles.css`, and `c15t/next/iab/styles.css` for IAB TCF |
110
- | TanStack Start | `options.styles` on `ConsentRoot` | `c15t/tanstack-start/styles.css`, and `c15t/tanstack-start/iab/styles.css` for IAB TCF |
111
- | React | `options.styles` on `ConsentProvider` | `c15t/react/styles.css`, and `c15t/react/iab/styles.css` for IAB TCF |
112
- | Svelte, SvelteKit | `options.styles` on `ConsentManagerProvider` | `@c15t/svelte/styles.css`, and `@c15t/svelte/iab/styles.css` for IAB TCF |
113
- | Astro | `styles` in the `c15t()` integration options | See [load Astro's stylesheets yourself](#load-astros-stylesheets-yourself) |
114
-
115
- ```tsx title="components/consent.tsx (partial)"
116
- <ConsentRoot
117
- state={state}
118
- config={consentConfig}
119
- options={{ styles: false }}
120
- >
107
+ | Framework | Option | Files to import |
108
+ | ----------------- | -------------------------------------------------------- | -------------------------------------------------------------------------------------- |
109
+ | Next.js | `options.styles` in `c15t.config.ts` or on `ConsentRoot` | `c15t/next/styles.css`, and `c15t/next/iab/styles.css` for IAB TCF |
110
+ | TanStack Start | `options.styles` on `ConsentRoot` | `c15t/tanstack-start/styles.css`, and `c15t/tanstack-start/iab/styles.css` for IAB TCF |
111
+ | React | `options.styles` on `ConsentProvider` | `c15t/react/styles.css`, and `c15t/react/iab/styles.css` for IAB TCF |
112
+ | Svelte, SvelteKit | `options.styles` on `ConsentProvider` or `ConsentRoot` | `@c15t/svelte/styles.css`, and `@c15t/svelte/iab/styles.css` for IAB TCF |
113
+ | Astro | `styles` in the `c15t()` integration options | See [load Astro's stylesheets yourself](#load-astros-stylesheets-yourself) |
114
+
115
+ ```ts title="c15t.config.ts (Next.js)"
116
+ import { defineConsentConfig } from 'c15t/next';
117
+
118
+ export default defineConsentConfig({ options: { styles: false } });
121
119
  ```
122
120
 
123
121
  `styles: false` turns off only the stylesheets. Tokens from `ConsentTheme`
@@ -111,9 +111,10 @@ Add `@tailwindcss/vite` under `vite.plugins` and list your CSS entry in
111
111
 
112
112
  ```ts title="nuxt.config.ts"
113
113
  import tailwindcss from '@tailwindcss/vite';
114
+ import { hosted } from 'c15t/vue';
114
115
 
115
116
  export default defineNuxtConfig({
116
- c15t: { backendURL: '/api/c15t' },
117
+ c15t: { backendURL: '/api/c15t', mode: hosted() },
117
118
  compatibilityDate: '2026-07-04',
118
119
  css: ['~/assets/css/main.css'],
119
120
  modules: ['c15t/vue'],
@@ -138,10 +139,12 @@ Tailwind:
138
139
  ```ts title="vite.config.ts"
139
140
  import tailwindcss from '@tailwindcss/vite';
140
141
  import vue from '@vitejs/plugin-vue';
141
- import c15tVue from 'c15t/vue/vite';
142
+ import { consentManifest } from 'c15t/vue/vite';
142
143
  import { defineConfig } from 'vite';
143
144
 
144
- export default defineConfig({ plugins: [vue(), c15tVue(), tailwindcss()] });
145
+ export default defineConfig({
146
+ plugins: [vue(), consentManifest({ backendURL: '/api/c15t' }), tailwindcss()],
147
+ });
145
148
  ```
146
149
 
147
150
  Import this stylesheet from `src/main.ts`:
@@ -272,9 +275,10 @@ They are the same plugin. A config that already lists
272
275
 
273
276
  Make four changes:
274
277
 
275
- 1. Set `styles: false` where you configure c15t: `options` on `ConsentRoot`
276
- in Next.js and TanStack Start, `options` on `ConsentProvider` in React,
277
- and `styles={false}` on `ConsentManagerProvider` in Svelte and SvelteKit.
278
+ 1. Set `styles: false` where you configure c15t: `options` in Next.js
279
+ `c15t.config.ts`, `options` on `ConsentRoot` in TanStack Start, `options`
280
+ on `ConsentProvider` in React, and `styles={false}` on `ConsentProvider` in
281
+ Svelte or `ConsentRoot` in SvelteKit.
278
282
  Vue, Nuxt and Astro need no change.
279
283
  2. Add the plugin to your PostCSS plugins, before `tailwindcss`. Write the plugins as an object, as the examples below do.
280
284
  PostCSS runs them in the order of the keys. Next.js also accepts an array
@@ -415,8 +419,10 @@ Nuxt reads PostCSS plugins from `postcss.plugins` in `nuxt.config.ts`, in
415
419
  the order listed:
416
420
 
417
421
  ```ts title="nuxt.config.ts"
422
+ import { hosted } from 'c15t/vue';
423
+
418
424
  export default defineNuxtConfig({
419
- c15t: { backendURL: '/api/c15t' },
425
+ c15t: { backendURL: '/api/c15t', mode: hosted() },
420
426
  compatibilityDate: '2026-07-04',
421
427
  css: ['~/assets/css/main.css'],
422
428
  modules: ['c15t/vue'],
@@ -536,7 +542,7 @@ export default {
536
542
 
537
543
  Import `@c15t/svelte/styles.css` above the directives, import this
538
544
  stylesheet from `src/main.ts`, and pass `styles={false}` to
539
- `ConsentManagerProvider`:
545
+ `ConsentProvider`:
540
546
 
541
547
  ```css title="src/app.css"
542
548
  /* Vite ignores an @import below other rules, so it goes first. */
@@ -572,7 +578,7 @@ export default {
572
578
 
573
579
  Import `@c15t/svelte/styles.css` above the directives, import this
574
580
  stylesheet from your root `+layout.svelte`, and pass `styles={false}` to
575
- `ConsentManagerProvider`:
581
+ `ConsentRoot`:
576
582
 
577
583
  ```css title="src/app.css"
578
584
  /* Vite ignores an @import below other rules, so it goes first. */
@@ -677,12 +683,12 @@ Add the classes to `components` in the `c15tVue` options. Vue takes
677
683
 
678
684
  ```ts title="src/main.ts"
679
685
  .use(c15tVue, {
680
- backendURL: '/api/c15t',
681
686
  components: {
682
687
  banner: {
683
688
  root: { class: 'p-[7px] dark:p-[11px]' },
684
689
  },
685
690
  },
691
+ mode: hosted(),
686
692
  })
687
693
  ```
688
694
 
@@ -816,7 +822,7 @@ instead:
816
822
  ```
817
823
 
818
824
  Import `@c15t/browser/styles.css` above the directives in your Tailwind entry.
819
- Install `@c15t/browser@alpha`, which provides both the stylesheet and
825
+ Install `@c15t/browser`, which provides both the stylesheet and
820
826
  `@c15t/browser/postcss-tailwind3`:
821
827
 
822
828
  ```css title="style.css"
@@ -40,13 +40,13 @@ export const theme = defineTheme({
40
40
  Install `@c15t/ui` if importing its theme helper directly. Where the theme
41
41
  goes depends on the framework:
42
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 |
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 `ConsentProvider` (Svelte) or `ConsentRoot` (SvelteKit) |
49
+ | HTML, JavaScript | `ui.theme` | Not available |
50
50
 
51
51
  React and Svelte providers do not turn tokens in their `theme` option into
52
52
  CSS, and warn in development when a theme holds tokens but the page has no
@@ -169,6 +169,35 @@ them, on any screen size.
169
169
  Test long translations and small screens after changing width or typography.
170
170
  A compact banner must still fit the required actions.
171
171
 
172
+ ## Stack the banner with your own layers
173
+
174
+ The banner and its overlay sit at the top of the page's stacking order by
175
+ default. Lower them when a navbar, drawer, or chat widget should cover the
176
+ banner.
177
+
178
+ | Variable | Default | Target |
179
+ | -------------------------------------- | -------- | ----------------------------- |
180
+ | `--consent-banner-z-index` | `999998` | Banner |
181
+ | `--consent-banner-overlay-z-index` | `999997` | Backdrop of a blocking banner |
182
+ | `--iab-consent-banner-z-index` | `999998` | IAB TCF banner |
183
+ | `--iab-consent-banner-overlay-z-index` | `999997` | IAB TCF banner backdrop |
184
+ | `--consent-dialog-z-index` | `999999` | Preferences dialog |
185
+ | `--consent-dialog-overlay-z-index` | `999998` | Preferences dialog backdrop |
186
+
187
+ ```css
188
+ :root {
189
+ --consent-banner-z-index: 40;
190
+ --consent-banner-overlay-z-index: 39;
191
+ }
192
+ ```
193
+
194
+ Keep each z-index above its overlay's, or the backdrop covers the card it
195
+ belongs to. Keep the dialog above the banner so Customize opens on top.
196
+
197
+ Only the card and the "Secured by" tag take clicks. Clicks on the space
198
+ around a floating card, or beside the tag on a bar, reach the page. A
199
+ blocking banner catches every click.
200
+
172
201
  ## Restyle the "Secured by" tag
173
202
 
174
203
  The tag sits on the edge of the banner and dialog cards and uses the primary
@@ -12,26 +12,30 @@ The [quickstart](../quickstart.md) mounts both in
12
12
  `src/consent.tsx`:
13
13
 
14
14
  ```tsx title="src/consent.tsx"
15
- import { manifest } from '@c15t/browser/headless';
15
+ import { posthog } from '@c15t/integrations/posthog';
16
16
  import {
17
17
  ConsentBanner,
18
18
  ConsentDialog,
19
19
  ConsentDialogLink,
20
20
  ConsentProvider,
21
+ hosted,
21
22
  } from 'c15t/react';
22
23
  import type { ReactNode } from 'react';
23
24
 
24
- import { consentManifest } from './c15t-manifest';
25
- import { scripts } from './scripts';
26
-
27
- const mode = manifest({
28
- backendURL:
29
- import.meta.env.VITE_C15T_BACKEND_URL ?? 'https://benchmarks-inth.inth.app',
30
- manifest: consentManifest,
31
- });
25
+ const options = {
26
+ // Asks VITE_C15T_BACKEND_URL for each visitor's policy.
27
+ mode: hosted(),
28
+ scripts: [
29
+ posthog({
30
+ id: 'phc_your_project_key',
31
+ initOptions: { cookieless_mode: 'never' },
32
+ loadMode: 'after-consent',
33
+ }),
34
+ ],
35
+ };
32
36
 
33
37
  export const Consent = ({ children }: { children: ReactNode }) => (
34
- <ConsentProvider options={{ mode, scripts }}>
38
+ <ConsentProvider options={options}>
35
39
  {children}
36
40
  <ConsentBanner />
37
41
  <ConsentDialog />
@@ -14,26 +14,30 @@ group: frameworks
14
14
  code.
15
15
 
16
16
  ```tsx title="src/consent.tsx"
17
- import { manifest } from '@c15t/browser/headless';
17
+ import { posthog } from '@c15t/integrations/posthog';
18
18
  import {
19
19
  ConsentBanner,
20
20
  ConsentDialog,
21
21
  ConsentDialogLink,
22
22
  ConsentProvider,
23
+ hosted,
23
24
  } from 'c15t/react';
24
25
  import type { ReactNode } from 'react';
25
26
 
26
- import { consentManifest } from './c15t-manifest';
27
- import { scripts } from './scripts';
28
-
29
- const mode = manifest({
30
- backendURL:
31
- import.meta.env.VITE_C15T_BACKEND_URL ?? 'https://benchmarks-inth.inth.app',
32
- manifest: consentManifest,
33
- });
27
+ const options = {
28
+ // Asks VITE_C15T_BACKEND_URL for each visitor's policy.
29
+ mode: hosted(),
30
+ scripts: [
31
+ posthog({
32
+ id: 'phc_your_project_key',
33
+ initOptions: { cookieless_mode: 'never' },
34
+ loadMode: 'after-consent',
35
+ }),
36
+ ],
37
+ };
34
38
 
35
39
  export const Consent = ({ children }: { children: ReactNode }) => (
36
- <ConsentProvider options={{ mode, scripts }}>
40
+ <ConsentProvider options={options}>
37
41
  {children}
38
42
  <ConsentBanner />
39
43
  <ConsentDialog />
@@ -14,26 +14,30 @@ The [quickstart](../quickstart.md) does this in
14
14
  `src/consent.tsx`:
15
15
 
16
16
  ```tsx title="src/consent.tsx"
17
- import { manifest } from '@c15t/browser/headless';
17
+ import { posthog } from '@c15t/integrations/posthog';
18
18
  import {
19
19
  ConsentBanner,
20
20
  ConsentDialog,
21
21
  ConsentDialogLink,
22
22
  ConsentProvider,
23
+ hosted,
23
24
  } from 'c15t/react';
24
25
  import type { ReactNode } from 'react';
25
26
 
26
- import { consentManifest } from './c15t-manifest';
27
- import { scripts } from './scripts';
28
-
29
- const mode = manifest({
30
- backendURL:
31
- import.meta.env.VITE_C15T_BACKEND_URL ?? 'https://benchmarks-inth.inth.app',
32
- manifest: consentManifest,
33
- });
27
+ const options = {
28
+ // Asks VITE_C15T_BACKEND_URL for each visitor's policy.
29
+ mode: hosted(),
30
+ scripts: [
31
+ posthog({
32
+ id: 'phc_your_project_key',
33
+ initOptions: { cookieless_mode: 'never' },
34
+ loadMode: 'after-consent',
35
+ }),
36
+ ],
37
+ };
34
38
 
35
39
  export const Consent = ({ children }: { children: ReactNode }) => (
36
- <ConsentProvider options={{ mode, scripts }}>
40
+ <ConsentProvider options={options}>
37
41
  {children}
38
42
  <ConsentBanner />
39
43
  <ConsentDialog />
@@ -44,11 +48,14 @@ export const Consent = ({ children }: { children: ReactNode }) => (
44
48
  );
45
49
  ```
46
50
 
47
- `manifest()` from `@c15t/browser/headless` resolves the policy from the
48
- snapshot the build bundled, and sends consent choices to `backendURL`. Replace
49
- the demo project URL with the URL from your Inth project, or set
50
- `VITE_C15T_BACKEND_URL`. To fetch the policy from the backend on every page
51
- load instead, pass `hosted({ url })` from `c15t/react`. The Next.js and TanStack Start adapters export `ConsentRoot` instead, which wraps
51
+ `hosted()` from `c15t/react` asks the backend's `/init` for the visitor's
52
+ policy on each page load, and sends consent choices to the same backend. It
53
+ uses the backend URL `consentManifest()` read from `VITE_C15T_BACKEND_URL` or
54
+ `VITE_INTH_PROJECT_URL`. Set one to the backend URL from your Inth project. To
55
+ resolve a bundled policy in the browser instead, pass `manifest()` from
56
+ `c15t/react`; see
57
+ [rendering](../rendering.md#bundle-the-policy-at-build-time).
58
+ The Next.js and TanStack Start adapters export `ConsentRoot` instead, which wraps
52
59
  this provider and passes it server-resolved state.
53
60
 
54
61
  ## Provider options
@@ -61,35 +68,35 @@ the provider to change them. `scripts`, `vendors`, `networkBlocker`,
61
68
  `iframeBlocker`, `consentCategories`, `overrides`, `user`, `callbacks` and
62
69
  `enabled` apply when they change.
63
70
 
64
- | Option | Purpose |
65
- | ----------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
66
- | `mode` | Required. `hosted({ url })` for a backend, `manifest()` from `@c15t/browser/headless` for a bundled policy, `offline()` for local policies, or `custom()` for your own transport |
67
- | `prefetch` | Server-resolved state, or a promise of it, from a framework adapter's server helper |
68
- | `scripts` | Consent-gated vendor scripts. See [scripts and embeds](../scripts.md) |
69
- | `vendors` | Vendors listed under their category with their own switch. See [vendor consent](../vendor-consent.md) |
70
- | `scriptLoader` | Options for the script loader module |
71
- | `networkBlocker` | Rules that hold `fetch` and `XMLHttpRequest` calls until their category is allowed, or `false` |
72
- | `iframeBlocker` | Gates DOM iframes that carry `data-category`. On by default; `false` turns it off |
73
- | `persistence` | Stores the visitor's choice in a cookie and localStorage. On by default |
74
- | `storageConfig` | Storage key and cookie settings for the stored choice |
75
- | `clearOnRevocation` | Cookies and storage keys to delete when a category is denied |
76
- | `reloadOnConsentRevoked` | Reload the page after the visitor turns off a granted category or vendor. Defaults to `true` |
77
- | `consentCategories` | Categories the preference dialog offers alongside those your scripts use, within the policy's scope |
78
- | `overrides` | Force the country, region or language the policy is resolved for, for testing |
79
- | `user` | Link consent records to a signed-in user |
80
- | `callbacks` | Choice, permission and error events |
81
- | `i18n` | Languages and message overrides |
82
- | `legalLinks` | Privacy policy and other links shown in the banner and dialog |
83
- | `theme` | `consentActions` and slot styles. Tokens go through `ConsentTheme`; see [customize](../customize.md) |
84
- | `components` | Slot attributes per component part |
85
- | `presentation` | Banner and dialog shape, position and blocking |
86
- | `experiment` | A/B test of `presentation`, read once at mount. See [banner experiments](../../../guides/banner-experiments.md) |
87
- | `journey` | Random id that links each `/init` to the save that follows. See [session reports](../../../concepts/data-fetching.md#link-an-init-to-the-save-that-follows) |
88
- | `colorScheme` | `'light'`, `'dark'`, `'system'`, or `null` to leave the `c15t-dark` class alone |
89
- | `noStyle`, `disableAnimation` | Drop the built-in styles, or turn off animations |
90
- | `preloadDialog` | When the deferred `ConsentDialog` starts loading: `'idle'` (default) or `'intent'`; see [ConsentDialog](./consent-dialog.md#behavior) |
91
- | `nonce` | Content Security Policy nonce for script elements c15t inserts |
92
- | `enabled` | Set `false` only when you deliberately bypass consent enforcement |
71
+ | Option | Purpose |
72
+ | ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
73
+ | `mode` | Required. `hosted()` for the backend's `/init`, `manifest()` for a bundled policy, `offline()` for local policies, all from `c15t/react`, or `custom()` for your own transport. See [consent modes](../../../concepts/modes.md) |
74
+ | `prefetch` | Server-resolved state, or a promise of it, from a framework adapter's server helper |
75
+ | `scripts` | Consent-gated vendor scripts. See [scripts and embeds](../scripts.md) |
76
+ | `vendors` | Vendors listed under their category with their own switch. See [vendor consent](../vendor-consent.md) |
77
+ | `scriptLoader` | Options for the script loader module |
78
+ | `networkBlocker` | Rules that hold `fetch` and `XMLHttpRequest` calls until their category is allowed, or `false` |
79
+ | `iframeBlocker` | Gates DOM iframes that carry `data-category`. On by default; `false` turns it off |
80
+ | `persistence` | Stores the visitor's choice in a cookie and localStorage. On by default |
81
+ | `storageConfig` | Storage key and cookie settings for the stored choice |
82
+ | `clearOnRevocation` | Cookies and storage keys to delete when a category is denied |
83
+ | `reloadOnConsentRevoked` | Reload the page after the visitor turns off a granted category or vendor. Defaults to `true` |
84
+ | `consentCategories` | Categories the preference dialog offers alongside those your scripts use, within the policy's scope |
85
+ | `overrides` | Force the country, region or language the policy is resolved for, for testing |
86
+ | `user` | Link consent records to a signed-in user |
87
+ | `callbacks` | Choice, permission and error events |
88
+ | `i18n` | Languages and message overrides |
89
+ | `legalLinks` | Privacy policy and other links shown in the banner and dialog |
90
+ | `theme` | `consentActions` and slot styles. Tokens go through `ConsentTheme`; see [customize](../customize.md) |
91
+ | `components` | Slot attributes per component part |
92
+ | `presentation` | Banner and dialog shape, position and blocking |
93
+ | `experiment` | A/B test of `presentation`, read once at mount. See [banner experiments](../../../guides/banner-experiments.md) |
94
+ | `journey` | Random id that links each `/init` to the save that follows. See [session reports](../../../concepts/data-fetching.md#link-an-init-to-the-save-that-follows) |
95
+ | `colorScheme` | `'light'`, `'dark'`, `'system'`, or `null` to leave the `c15t-dark` class alone |
96
+ | `noStyle`, `disableAnimation` | Drop the built-in styles, or turn off animations |
97
+ | `preloadDialog` | When the deferred `ConsentDialog` starts loading: `'idle'` (default) or `'intent'`; see [ConsentDialog](./consent-dialog.md#behavior) |
98
+ | `nonce` | Content Security Policy nonce for script elements c15t inserts |
99
+ | `enabled` | Set `false` only when you deliberately bypass consent enforcement |
93
100
 
94
101
  `enabled: false` grants categories and allows gated loading while suppressing
95
102
  consent UI. It is not a way to fix failed initialization or hide a banner in
@@ -36,26 +36,30 @@ Most apps start with four components. `ConsentProvider` holds the runtime,
36
36
  one file:
37
37
 
38
38
  ```tsx title="src/consent.tsx"
39
- import { manifest } from '@c15t/browser/headless';
39
+ import { posthog } from '@c15t/integrations/posthog';
40
40
  import {
41
41
  ConsentBanner,
42
42
  ConsentDialog,
43
43
  ConsentDialogLink,
44
44
  ConsentProvider,
45
+ hosted,
45
46
  } from 'c15t/react';
46
47
  import type { ReactNode } from 'react';
47
48
 
48
- import { consentManifest } from './c15t-manifest';
49
- import { scripts } from './scripts';
50
-
51
- const mode = manifest({
52
- backendURL:
53
- import.meta.env.VITE_C15T_BACKEND_URL ?? 'https://benchmarks-inth.inth.app',
54
- manifest: consentManifest,
55
- });
49
+ const options = {
50
+ // Asks VITE_C15T_BACKEND_URL for each visitor's policy.
51
+ mode: hosted(),
52
+ scripts: [
53
+ posthog({
54
+ id: 'phc_your_project_key',
55
+ initOptions: { cookieless_mode: 'never' },
56
+ loadMode: 'after-consent',
57
+ }),
58
+ ],
59
+ };
56
60
 
57
61
  export const Consent = ({ children }: { children: ReactNode }) => (
58
- <ConsentProvider options={{ mode, scripts }}>
62
+ <ConsentProvider options={options}>
59
63
  {children}
60
64
  <ConsentBanner />
61
65
  <ConsentDialog />
@@ -10,14 +10,16 @@ group: frameworks
10
10
 
11
11
  A React app with c15t adds these to the page, and your policy must allow them:
12
12
 
13
- | What | Directive |
14
- | --------------------------------------------------------------------------------------------- | ----------------------------------------------------- |
15
- | The `<style>` elements the banner, dialog and other stock components render with c15t's rules | `style-src` with a nonce |
16
- | `c15t/react/styles.css`, only if you import it with `styles: false` | `style-src 'self'` |
17
- | The `<style id="c15t-theme">` element, if you render `ConsentTheme` | `style-src` with a nonce or its hash |
18
- | `<script>` elements the script loader creates for `scripts` entries, both `src` and inline | `script-src` with each vendor's host, or a nonce |
19
- | Browser requests to your backend's `/init` and `/subjects` | `connect-src` with the backend origin |
20
- | Iframes inside `ConsentGate`, and the frames, images and requests your vendors load | `frame-src`, `img-src`, `connect-src` for each vendor |
13
+ | What | Directive |
14
+ | --------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------- |
15
+ | The `<style>` elements the banner, dialog and other stock components render with c15t's rules | `style-src` with a nonce |
16
+ | `c15t/react/styles.css`, only if you import it with `styles: false` | `style-src 'self'` |
17
+ | The `<style id="c15t-theme">` element, if you render `ConsentTheme` | `style-src` with a nonce or its hash |
18
+ | `<script>` elements the script loader creates for `scripts` entries, both `src` and inline | `script-src` with each vendor's host, or a nonce |
19
+ | The inline `<script>` that `consentManifest()` adds to `index.html` to send `/init` early | `script-src` with a nonce or its hash |
20
+ | With server rendering, the inline `<script>` before the banner's buttons that holds a tap made before hydration | `script-src` with a nonce or its hash |
21
+ | Browser requests to your backend's `/init` and `/subjects` | `connect-src` with the backend origin |
22
+ | Iframes inside `ConsentGate`, and the frames, images and requests your vendors load | `frame-src`, `img-src`, `connect-src` for each vendor |
21
23
 
22
24
  In a client-rendered app, c15t components set their inline styles through
23
25
  React. A policy does not restrict styles set that way, so they need no
@@ -25,8 +27,10 @@ directive.
25
27
 
26
28
  ## Allow the consent backend
27
29
 
28
- With `hosted({ url })`, the browser calls `GET {url}/init` on every page load
29
- and `POST {url}/subjects` when the visitor accepts, rejects or saves. Add the
30
+ With `hosted()`, the browser calls `GET {url}/init` on every page load
31
+ and `POST {url}/subjects` when the visitor accepts, rejects or saves. With
32
+ `manifest()`, it calls `POST {url}/subjects`, and `GET {url}/init` only when
33
+ the policy needs a location the browser does not know. Add the
30
34
  backend's origin to `connect-src`:
31
35
 
32
36
  ```txt title="Content-Security-Policy (partial)"
@@ -67,8 +71,9 @@ entry takes precedence for that element. The stock components put it on the
67
71
  With Vite, set `html.cspNonce` to a placeholder in `vite.config.ts`, and have
68
72
  your server replace it with a new nonce on every response. Vite then adds
69
73
  `<meta property="csp-nonce" nonce="...">` to the page, and the browser keeps the
70
- value in the tag's `nonce` property. Read it where you create the provider, in
71
- the quickstart's `src/consent.tsx`:
74
+ value in the tag's `nonce` property. Vite also stamps the nonce on the early
75
+ `/init` script. Read it where you create the provider, in the
76
+ quickstart's `src/consent.tsx`:
72
77
 
73
78
  ```tsx title="src/consent.tsx (partial)"
74
79
  const nonce =
@@ -85,6 +90,10 @@ The provider reads `options.nonce` once, when it mounts. A server-rendered
85
90
  React app, such as one built with React Router framework mode, reads the nonce
86
91
  from its own request and passes it the same way.
87
92
 
93
+ On a static host, allow the early `/init` script by the hash in the browser's
94
+ `Refused to execute inline script` error. If the policy blocks it, the app
95
+ sends `/init` itself.
96
+
88
97
  ## Allow c15t's style elements
89
98
 
90
99
  The banner, dialog and other stock components render c15t's rules as
@@ -65,6 +65,6 @@ Use focused hooks for feature gates. For a complete custom prompt or preference
65
65
  center, use `useHeadlessConsentUI()` for surface actions and `useConsentDraft()`
66
66
  for staged choices, inside one `ConsentDraftProvider` so the save commits the
67
67
  same draft. v3 has no `useConsentManager()`; the
68
- [upgrade guide](../../upgrade-v3.md#replace-useconsentmanager) maps each of its
69
- fields to a hook. Read [how consent works](../../concepts/how-consent-works.md#a-permission-is-not-a-recorded-choice) before
68
+ [`use-consent-manager-to-hooks` codemod](https://c15t.com/docs/cli/commands/codemods#migrate-useconsentmanager-to-v3-hooks)
69
+ rewrites most of its calls to hooks. Read [how consent works](../../concepts/how-consent-works.md#a-permission-is-not-a-recorded-choice) before
70
70
  treating a callback as evidence of a choice.