@c15t/react 3.0.0-alpha.8 → 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 (164) hide show
  1. package/AGENTS.md +10 -12
  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/prompt/prompt.js +1 -1
  20. package/dist/components/shared/dialog-sheets.js +1 -0
  21. package/dist/components/shared/first-paint-sheets.js +1 -0
  22. package/dist/components/shared/iab-dialog-sheets.js +1 -0
  23. package/dist/components/shared/iab-first-paint-sheets.js +1 -0
  24. package/dist/components/shared/primitives/button.js +1 -1
  25. package/dist/components/shared/surface-styles.js +2 -0
  26. package/dist/consent-gate.js +1 -1
  27. package/dist/context/index.js +1 -1
  28. package/dist/devtools.js +1 -1
  29. package/dist/hooks/use-iab-policy-guard.js +1 -0
  30. package/dist/iab/styles.tw3.css +2 -2
  31. package/dist/iab-context.js +1 -1
  32. package/dist/iab-dialog-deferred.js +2 -0
  33. package/dist/iab.js +1 -1
  34. package/dist/index-parts/consent-gate.js +1 -1
  35. package/dist/index-parts/core.js +1 -1
  36. package/dist/index-parts/frame.js +1 -0
  37. package/dist/index-parts/hosted.js +1 -0
  38. package/dist/index-parts/offline.js +1 -0
  39. package/dist/index-parts/presets.js +1 -0
  40. package/dist/index.js +1 -1
  41. package/dist/modes.js +1 -0
  42. package/dist/panel-link.js +1 -1
  43. package/dist/panel-trigger.js +1 -1
  44. package/dist/provider.js +1 -1
  45. package/dist/styles.tw3.css +2 -2
  46. package/dist/transports/hosted.js +1 -0
  47. package/dist/transports/manifest.js +1 -0
  48. package/dist/utils/deferred-module.js +1 -1
  49. package/dist/version.js +1 -1
  50. package/dist-types/chunk-warming.d.ts +7 -6
  51. package/dist-types/components/consent-gate/frame.d.ts +19 -0
  52. package/dist-types/components/consent-gate/index.d.ts +3 -19
  53. package/dist-types/components/iab-prompt/iab-prompt.d.ts +1 -1
  54. package/dist-types/components/panel/index.d.ts +1 -1
  55. package/dist-types/components/panel-trigger/toolbar-loader.d.ts +34 -0
  56. package/dist-types/components/preferences/index.d.ts +1 -1
  57. package/dist-types/components/prompt/atoms/early-tap-script.d.ts +17 -0
  58. package/dist-types/components/prompt/index.d.ts +1 -1
  59. package/dist-types/components/shared/dialog-sheets.d.ts +9 -0
  60. package/dist-types/components/shared/first-paint-sheets.d.ts +8 -0
  61. package/dist-types/components/shared/iab-dialog-sheets.d.ts +3 -0
  62. package/dist-types/components/shared/iab-first-paint-sheets.d.ts +3 -0
  63. package/dist-types/components/shared/surface-styles.d.ts +60 -0
  64. package/dist-types/consent-gate.d.ts +1 -1
  65. package/dist-types/context/index.d.ts +3 -3
  66. package/dist-types/context.d.ts +2 -0
  67. package/dist-types/gpp.d.ts +1 -1
  68. package/dist-types/hooks/use-iab-policy-guard.d.ts +35 -0
  69. package/dist-types/iab-dialog-deferred.d.ts +36 -0
  70. package/dist-types/iab.d.ts +3 -2
  71. package/dist-types/index-parts/consent-gate.d.ts +1 -1
  72. package/dist-types/index-parts/core.d.ts +1 -2
  73. package/dist-types/index-parts/frame.d.ts +7 -0
  74. package/dist-types/index-parts/hosted.d.ts +7 -0
  75. package/dist-types/index-parts/offline.d.ts +7 -0
  76. package/dist-types/index-parts/presets.d.ts +7 -0
  77. package/dist-types/index.d.ts +5 -1
  78. package/dist-types/modes.d.ts +26 -0
  79. package/dist-types/panel-link.d.ts +1 -1
  80. package/dist-types/panel-trigger.d.ts +1 -1
  81. package/dist-types/provider.d.ts +22 -4
  82. package/dist-types/transports/hosted.d.ts +36 -0
  83. package/dist-types/transports/manifest.d.ts +48 -0
  84. package/dist-types/ui-config-context.d.ts +4 -0
  85. package/dist-types/version.d.ts +1 -1
  86. package/docs/README.md +10 -12
  87. package/docs/concepts/choose-your-setup.md +45 -8
  88. package/docs/concepts/consent-categories.md +1 -1
  89. package/docs/concepts/consent-state.md +5 -2
  90. package/docs/concepts/data-fetching.md +100 -40
  91. package/docs/concepts/modes.md +281 -0
  92. package/docs/customization/dark-mode.md +1 -1
  93. package/docs/customization/motion.md +2 -2
  94. package/docs/customization/overview.md +3 -2
  95. package/docs/customization/recipes.md +4 -4
  96. package/docs/customization/slots.md +25 -25
  97. package/docs/customization/stylesheets.md +161 -50
  98. package/docs/customization/tailwind.md +71 -50
  99. package/docs/customization/tokens.md +42 -13
  100. package/docs/frameworks/react/components/consent-banner.md +14 -12
  101. package/docs/frameworks/react/components/consent-dialog.md +15 -13
  102. package/docs/frameworks/react/components/consent-provider.md +51 -46
  103. package/docs/frameworks/react/components.md +16 -13
  104. package/docs/frameworks/react/compose.md +3 -3
  105. package/docs/frameworks/react/content-security-policy.md +35 -12
  106. package/docs/frameworks/react/customize.md +21 -13
  107. package/docs/frameworks/react/hooks.md +2 -2
  108. package/docs/frameworks/react/iab.md +56 -6
  109. package/docs/frameworks/react/quickstart.md +65 -99
  110. package/docs/frameworks/react/rendering.md +170 -94
  111. package/docs/frameworks/react/scripts.md +19 -30
  112. package/docs/frameworks/react/troubleshooting.md +100 -32
  113. package/docs/frameworks/react/upgrade-v3.md +133 -79
  114. package/docs/frameworks/react/vendor-consent.md +19 -7
  115. package/docs/guides/banner-experiments.md +21 -8
  116. package/docs/guides/troubleshooting.md +90 -26
  117. package/docs/guides/verify-consent.md +23 -8
  118. package/docs/integrations/adobe-analytics.md +60 -64
  119. package/docs/integrations/ahrefs-analytics.md +58 -63
  120. package/docs/integrations/amplitude.md +60 -64
  121. package/docs/integrations/clearbit.md +59 -63
  122. package/docs/integrations/cloudflare-web-analytics.md +56 -60
  123. package/docs/integrations/cloudflare-zaraz.md +55 -57
  124. package/docs/integrations/crisp.md +64 -69
  125. package/docs/integrations/databuddy.md +62 -64
  126. package/docs/integrations/fathom-analytics.md +56 -60
  127. package/docs/integrations/front-chat.md +56 -60
  128. package/docs/integrations/google-maps.md +25 -13
  129. package/docs/integrations/google-tag-manager.md +153 -77
  130. package/docs/integrations/google-tag.md +134 -78
  131. package/docs/integrations/heap.md +56 -60
  132. package/docs/integrations/hightouch.md +55 -60
  133. package/docs/integrations/hotjar.md +60 -64
  134. package/docs/integrations/intercom.md +55 -60
  135. package/docs/integrations/klaviyo.md +65 -66
  136. package/docs/integrations/linkedin-insights.md +58 -63
  137. package/docs/integrations/logrocket.md +55 -60
  138. package/docs/integrations/matomo-analytics.md +58 -60
  139. package/docs/integrations/meta-pixel.md +56 -60
  140. package/docs/integrations/microsoft-clarity.md +57 -60
  141. package/docs/integrations/microsoft-uet.md +58 -61
  142. package/docs/integrations/mixpanel-analytics.md +73 -75
  143. package/docs/integrations/one-dollar-stats.md +53 -59
  144. package/docs/integrations/openai-pixel.md +58 -60
  145. package/docs/integrations/overview.md +59 -42
  146. package/docs/integrations/pinterest-tag.md +56 -60
  147. package/docs/integrations/pirsch.md +54 -60
  148. package/docs/integrations/plausible-analytics.md +55 -60
  149. package/docs/integrations/posthog.md +56 -60
  150. package/docs/integrations/promptwatch.md +57 -63
  151. package/docs/integrations/reddit-pixel.md +57 -60
  152. package/docs/integrations/rudderstack.md +65 -67
  153. package/docs/integrations/rybbit-analytics.md +70 -75
  154. package/docs/integrations/segment.md +56 -60
  155. package/docs/integrations/sentry.md +65 -60
  156. package/docs/integrations/snapchat-pixel.md +54 -60
  157. package/docs/integrations/tiktok-pixel.md +59 -63
  158. package/docs/integrations/umami-analytics.md +54 -60
  159. package/docs/integrations/vercel-analytics.md +53 -59
  160. package/docs/integrations/vercel-speed-insights.md +317 -0
  161. package/docs/integrations/x-pixel.md +57 -63
  162. package/docs/integrations/youtube.md +25 -13
  163. package/package.json +18 -8
  164. package/docs/upgrade-v3.md +0 -429
@@ -8,14 +8,14 @@ group: customization
8
8
  ## Where tokens come from
9
9
 
10
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
11
+ of their fonts, radii, spacing, shadows and motion too. c15t's rules set the
12
+ defaults. You change them with a theme object or with CSS, and every part
13
13
  that reads a token changes with it. Some values are still fixed for one
14
14
  component, such as button padding, the radius of the "Secured by" tag and
15
15
  several font sizes and weights, so a token change does not move them. Restyle
16
16
  those parts through [slots](./slots.md) or your own CSS.
17
17
  [Stylesheets and CSS layers](./stylesheets.md)
18
- covers which stylesheet to load, and [dark mode](./dark-mode.md)
18
+ covers how c15t loads its rules, and [dark mode](./dark-mode.md)
19
19
  covers the dark set of tokens.
20
20
 
21
21
  ## Set semantic values together
@@ -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
@@ -59,9 +59,9 @@ setup.
59
59
  ## Combine a generated theme with your own CSS
60
60
 
61
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
62
+ `.c15t-theme-root.c15t-theme-root`, more specific than c15t's defaults. The
63
+ theme therefore overrides the defaults whether its
64
+ `<style>` element comes before or after c15t's rules. The same output backs
65
65
  `ConsentTheme` in React, Next.js and TanStack Start, Astro's `theme` option and
66
66
  the script tag's `ui.theme`.
67
67
 
@@ -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,28 +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
- import 'c15t/react/styles.css';
28
-
29
- const mode = manifest({
30
- backendURL:
31
- import.meta.env.VITE_C15T_BACKEND_URL ?? 'https://benchmarks-inth.inth.app',
32
- manifest: consentManifest,
33
- });
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
+ };
34
36
 
35
37
  export const Consent = ({ children }: { children: ReactNode }) => (
36
- <ConsentProvider options={{ mode, scripts }}>
38
+ <ConsentProvider options={options}>
37
39
  {children}
38
40
  <ConsentBanner />
39
41
  <ConsentDialog />
@@ -14,28 +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
- import 'c15t/react/styles.css';
30
-
31
- const mode = manifest({
32
- backendURL:
33
- import.meta.env.VITE_C15T_BACKEND_URL ?? 'https://benchmarks-inth.inth.app',
34
- manifest: consentManifest,
35
- });
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
+ };
36
38
 
37
39
  export const Consent = ({ children }: { children: ReactNode }) => (
38
- <ConsentProvider options={{ mode, scripts }}>
40
+ <ConsentProvider options={options}>
39
41
  {children}
40
42
  <ConsentBanner />
41
43
  <ConsentDialog />
@@ -135,7 +137,7 @@ If a preload fails, for example because the visitor is offline, nothing is
135
137
  cached: the next hover, focus or open tries the download again.
136
138
 
137
139
  The dialog's CSS travels in the same chunk and is applied before the dialog
138
- renders, so your app's `styles.css` import stays small.
140
+ renders, so the first page load carries only the banner's rules.
139
141
 
140
142
  Without an `open` prop, Escape closes the dialog without saving. If the
141
143
  policy still owes a choice, the banner comes back. Save Settings, Accept All and
@@ -14,28 +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
- import 'c15t/react/styles.css';
30
-
31
- const mode = manifest({
32
- backendURL:
33
- import.meta.env.VITE_C15T_BACKEND_URL ?? 'https://benchmarks-inth.inth.app',
34
- manifest: consentManifest,
35
- });
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
+ };
36
38
 
37
39
  export const Consent = ({ children }: { children: ReactNode }) => (
38
- <ConsentProvider options={{ mode, scripts }}>
40
+ <ConsentProvider options={options}>
39
41
  {children}
40
42
  <ConsentBanner />
41
43
  <ConsentDialog />
@@ -46,11 +48,14 @@ export const Consent = ({ children }: { children: ReactNode }) => (
46
48
  );
47
49
  ```
48
50
 
49
- `manifest()` from `@c15t/browser/headless` resolves the policy from the
50
- snapshot the build bundled, and sends consent choices to `backendURL`. Replace
51
- the demo project URL with the URL from your Inth project, or set
52
- `VITE_C15T_BACKEND_URL`. To fetch the policy from the backend on every page
53
- 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
54
59
  this provider and passes it server-resolved state.
55
60
 
56
61
  ## Provider options
@@ -63,35 +68,35 @@ the provider to change them. `scripts`, `vendors`, `networkBlocker`,
63
68
  `iframeBlocker`, `consentCategories`, `overrides`, `user`, `callbacks` and
64
69
  `enabled` apply when they change.
65
70
 
66
- | Option | Purpose |
67
- | ----------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
68
- | `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 |
69
- | `prefetch` | Server-resolved state, or a promise of it, from a framework adapter's server helper |
70
- | `scripts` | Consent-gated vendor scripts. See [scripts and embeds](../scripts.md) |
71
- | `vendors` | Vendors listed under their category with their own switch. See [vendor consent](../vendor-consent.md) |
72
- | `scriptLoader` | Options for the script loader module |
73
- | `networkBlocker` | Rules that hold `fetch` and `XMLHttpRequest` calls until their category is allowed, or `false` |
74
- | `iframeBlocker` | Gates DOM iframes that carry `data-category`. On by default; `false` turns it off |
75
- | `persistence` | Stores the visitor's choice in a cookie and localStorage. On by default |
76
- | `storageConfig` | Storage key and cookie settings for the stored choice |
77
- | `clearOnRevocation` | Cookies and storage keys to delete when a category is denied |
78
- | `reloadOnConsentRevoked` | Reload the page after the visitor turns off a granted category or vendor. Defaults to `true` |
79
- | `consentCategories` | Categories the preference dialog offers alongside those your scripts use, within the policy's scope |
80
- | `overrides` | Force the country, region or language the policy is resolved for, for testing |
81
- | `user` | Link consent records to a signed-in user |
82
- | `callbacks` | Choice, permission and error events |
83
- | `i18n` | Languages and message overrides |
84
- | `legalLinks` | Privacy policy and other links shown in the banner and dialog |
85
- | `theme` | `consentActions` and slot styles. Tokens go through `ConsentTheme`; see [customize](../customize.md) |
86
- | `components` | Slot attributes per component part |
87
- | `presentation` | Banner and dialog shape, position and blocking |
88
- | `experiment` | A/B test of `presentation`, read once at mount. See [banner experiments](../../../guides/banner-experiments.md) |
89
- | `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) |
90
- | `colorScheme` | `'light'`, `'dark'`, `'system'`, or `null` to leave the `c15t-dark` class alone |
91
- | `noStyle`, `disableAnimation` | Drop the built-in styles, or turn off animations |
92
- | `preloadDialog` | When the deferred `ConsentDialog` starts loading: `'idle'` (default) or `'intent'`; see [ConsentDialog](./consent-dialog.md#behavior) |
93
- | `nonce` | Content Security Policy nonce for script elements c15t inserts |
94
- | `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 |
95
100
 
96
101
  `enabled: false` grants categories and allows gated loading while suppressing
97
102
  consent UI. It is not a way to fix failed initialization or hide a banner in
@@ -36,28 +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
- import 'c15t/react/styles.css';
52
-
53
- const mode = manifest({
54
- backendURL:
55
- import.meta.env.VITE_C15T_BACKEND_URL ?? 'https://benchmarks-inth.inth.app',
56
- manifest: consentManifest,
57
- });
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
+ };
58
60
 
59
61
  export const Consent = ({ children }: { children: ReactNode }) => (
60
- <ConsentProvider options={{ mode, scripts }}>
62
+ <ConsentProvider options={options}>
61
63
  {children}
62
64
  <ConsentBanner />
63
65
  <ConsentDialog />
@@ -84,7 +86,8 @@ explains the trade-off.
84
86
 
85
87
  Import `IABProvider`, `IABConsentBanner` and `IABConsentDialog` from
86
88
  `c15t/react/iab`, render them inside `ConsentProvider` next to
87
- `ConsentBanner` and `ConsentDialog`, and load `c15t/react/iab/styles.css`. The stock banner and dialog stay closed
89
+ `ConsentBanner` and `ConsentDialog`, and load `c15t/react/styles.css` and
90
+ `c15t/react/iab/styles.css` with `styles: false`. The stock banner and dialog stay closed
88
91
  under an IAB policy. See
89
92
  [IAB TCF](./iab.md).
90
93
 
@@ -315,9 +315,9 @@ banner keeps them as long as you keep the parts that own them:
315
315
 
316
316
  ## Keep theme tokens and slots working
317
317
 
318
- The parts render the stock class names, so the c15t stylesheet and its
319
- `--c15t-*` tokens style a composed banner the way they style the stock one.
320
- Keep the stylesheet import.
318
+ The parts render the stock class names, and the banner's `Root` renders
319
+ c15t's rules, so c15t's styles and `--c15t-*` tokens style a composed
320
+ banner the way they style the stock one.
321
321
 
322
322
  Slots in the provider's `options.components` reach each part by key:
323
323
  `banner.root`, `banner.card`, `banner.header`, `banner.title`,
@@ -10,13 +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
- | `c15t/react/styles.css`, which your bundler emits with your own CSS | `style-src 'self'` |
16
- | The `<style id="c15t-theme">` element, if you render `ConsentTheme` | `style-src` with a nonce or its hash |
17
- | `<script>` elements the script loader creates for `scripts` entries, both `src` and inline | `script-src` with each vendor's host, or a nonce |
18
- | Browser requests to your backend's `/init` and `/subjects` | `connect-src` with the backend origin |
19
- | 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 |
20
23
 
21
24
  In a client-rendered app, c15t components set their inline styles through
22
25
  React. A policy does not restrict styles set that way, so they need no
@@ -24,8 +27,10 @@ directive.
24
27
 
25
28
  ## Allow the consent backend
26
29
 
27
- With `hosted({ url })`, the browser calls `GET {url}/init` on every page load
28
- 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
29
34
  backend's origin to `connect-src`:
30
35
 
31
36
  ```txt title="Content-Security-Policy (partial)"
@@ -60,13 +65,15 @@ when a server renders or rewrites your `index.html` per request.
60
65
 
61
66
  Pass the nonce as `options.nonce` on `ConsentProvider`. The script loader
62
67
  stamps it on every `<script>` it creates. A `nonce` on a single `scripts`
63
- entry takes precedence for that element.
68
+ entry takes precedence for that element. The stock components put it on the
69
+ `<style>` elements that carry c15t's rules.
64
70
 
65
71
  With Vite, set `html.cspNonce` to a placeholder in `vite.config.ts`, and have
66
72
  your server replace it with a new nonce on every response. Vite then adds
67
73
  `<meta property="csp-nonce" nonce="...">` to the page, and the browser keeps the
68
- value in the tag's `nonce` property. Read it where you create the provider, in
69
- 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`:
70
77
 
71
78
  ```tsx title="src/consent.tsx (partial)"
72
79
  const nonce =
@@ -83,6 +90,22 @@ The provider reads `options.nonce` once, when it mounts. A server-rendered
83
90
  React app, such as one built with React Router framework mode, reads the nonce
84
91
  from its own request and passes it the same way.
85
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
+
97
+ ## Allow c15t's style elements
98
+
99
+ The banner, dialog and other stock components render c15t's rules as
100
+ `<style>` elements. A `style-src` without `'unsafe-inline'` must admit them.
101
+ Either:
102
+
103
+ * Set `options.nonce` on `ConsentProvider`, as in [use a nonce](#use-a-nonce).
104
+ The elements then render next to each component with the nonce. React 19
105
+ would otherwise move them into `<head>` and drop the nonce.
106
+ * Or set `styles: false` in the provider options and import
107
+ `c15t/react/styles.css` from your bundle. `style-src 'self'` admits it.
108
+
86
109
  ## Allow the theme style element
87
110
 
88
111
  `ConsentTheme` renders your theme tokens into a `<style id="c15t-theme">`
@@ -1,8 +1,8 @@
1
1
  ---
2
2
  title: Customize
3
3
  description: Change the colors, fonts, layout, button styles and copy of the
4
- c15t banner and dialog in a React app with the stylesheet, theme tokens,
5
- provider options and slots.
4
+ c15t banner and dialog in a React app with theme tokens, provider options and
5
+ slots, and load the stylesheet yourself when you need to.
6
6
  group: frameworks
7
7
  ---
8
8
 
@@ -24,26 +24,35 @@ across frameworks.
24
24
  See the [design gallery](../../customization/recipes.md) for five banner designs,
25
25
  from a bottom bar to a fully custom one, with tested code for this framework.
26
26
 
27
- ## Import the stylesheet
27
+ ## Import the stylesheet yourself
28
28
 
29
- Import the stylesheet once, in the module that renders `ConsentProvider`. The
30
- [quickstart](./quickstart.md) does this in `src/consent.tsx`:
29
+ A React app imports no c15t stylesheet. `ConsentBanner`, `ConsentDialog` and
30
+ the other stock components render the rules they use as `<style>` elements.
31
31
 
32
- ```tsx
32
+ Import the stylesheet yourself with Tailwind CSS 3, to put c15t's rules in a
33
+ named cascade layer. Set `styles: false` in the provider
34
+ options, so the components add no second copy, and import the stylesheet once
35
+ in the module that renders `ConsentProvider`:
36
+
37
+ ```tsx title="src/consent.tsx (partial)"
33
38
  import 'c15t/react/styles.css';
39
+
40
+ // In the ConsentProvider options:
41
+ styles: false,
34
42
  ```
35
43
 
36
- The import is the same with or without Tailwind. To import the stylesheet from
37
- CSS instead, put it at the top of your global stylesheet, above any
38
- `@tailwind` directives. `postcss-import`, which Vite uses, ignores an
39
- `@import` that follows other rules. Tailwind 3 also needs a PostCSS plugin;
44
+ To import the stylesheet from CSS instead, put it at the top of your global
45
+ stylesheet, above any `@tailwind` directives. `postcss-import`, which Vite
46
+ uses, ignores an `@import` that follows other rules. Tailwind 3 also needs a
47
+ PostCSS plugin;
40
48
  [Tailwind CSS](../../customization/tailwind.md#set-up-tailwind-css-3) shows the
41
49
  setup.
42
50
 
43
51
  ## Change colors, fonts and radius
44
52
 
45
53
  Colors, radii, shadows, typography, and motion come from `--c15t-*` custom
46
- properties. The package stylesheet sets the default values. To change them,
54
+ properties. The rules the components render set the default values, as
55
+ does `styles.css` when you import it with `styles: false`. To change them,
47
56
  render `ConsentTheme` with your theme where your app renders on the server,
48
57
  or override the variables directly in your CSS.
49
58
 
@@ -80,8 +89,7 @@ inside `<style>`.
80
89
 
81
90
  A single-page app has no server render, so pick one of two ways:
82
91
 
83
- * **Set the variables in your CSS.** Nothing extra ships to the browser. Load
84
- this after `c15t/react/styles.css`:
92
+ * **Set the variables in your CSS.** Nothing extra ships to the browser:
85
93
 
86
94
  ```css title="src/index.css"
87
95
  :root {
@@ -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.