@c15t/react 3.0.0-alpha.3 → 3.0.0-alpha.5

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 (219) hide show
  1. package/AGENTS.md +92 -72
  2. package/README.md +21 -17
  3. package/SKILL.md +34 -0
  4. package/dist/component-hooks/use-headless-consent-ui.js +1 -1
  5. package/dist/component-hooks/use-headless-iab-consent-ui.js +1 -1
  6. package/dist/components/consent-gate/atoms.js +1 -1
  7. package/dist/components/consent-gate/consent-gate.js +1 -1
  8. package/dist/components/iab-panel/atoms/card.js +1 -1
  9. package/dist/components/iab-panel/atoms/feature-item.js +2 -0
  10. package/dist/components/iab-panel/atoms/header.js +1 -1
  11. package/dist/components/iab-panel/iab-panel.js +1 -1
  12. package/dist/components/iab-panel/index.js +1 -1
  13. package/dist/components/iab-prompt/atoms/overlay.js +1 -1
  14. package/dist/components/iab-prompt/atoms/root.js +1 -1
  15. package/dist/components/panel/atoms/card.js +1 -1
  16. package/dist/components/panel/atoms/root.js +1 -1
  17. package/dist/components/panel-trigger/atoms/button.js +1 -1
  18. package/dist/components/panel-trigger/atoms/toolbar.js +1 -1
  19. package/dist/components/preferences/atoms/accordion.js +1 -1
  20. package/dist/components/prompt/atoms/overlay.js +1 -1
  21. package/dist/components/prompt/atoms/root.js +1 -1
  22. package/dist/components/prompt/components.js +1 -1
  23. package/dist/components/shared/libs/compose-refs.js +1 -0
  24. package/dist/components/shared/libs/slot.js +1 -1
  25. package/dist/components/shared/primitives/button.js +1 -1
  26. package/dist/components/shared/ui/dialog/dialog.js +1 -1
  27. package/dist/components/shared/ui/icon.js +1 -1
  28. package/dist/context/consent-tracking-context.js +1 -1
  29. package/dist/devtools.js +1 -1
  30. package/dist/draft-context.js +2 -0
  31. package/dist/draft.js +1 -1
  32. package/dist/external-iab-context.js +1 -1
  33. package/dist/gpp.js +2 -0
  34. package/dist/hooks/use-color-scheme.js +1 -1
  35. package/dist/hooks/use-focus-trap.js +1 -1
  36. package/dist/hooks.js +1 -1
  37. package/dist/iab/styles.tw3.css +4 -4
  38. package/dist/index-parts/consent-banner.js +1 -0
  39. package/dist/index-parts/consent-dialog-link.js +1 -0
  40. package/dist/index-parts/consent-dialog-trigger.js +1 -0
  41. package/dist/index-parts/consent-dialog.js +1 -0
  42. package/dist/index-parts/consent-gate.js +1 -0
  43. package/dist/index-parts/core.js +1 -0
  44. package/dist/index-parts/draft.js +1 -0
  45. package/dist/index-parts/hooks.js +1 -0
  46. package/dist/index-parts/module-hooks.js +1 -0
  47. package/dist/index-parts/provider.js +1 -0
  48. package/dist/index-parts/theme.js +1 -0
  49. package/dist/index-parts/translations.js +1 -0
  50. package/dist/index.js +1 -1
  51. package/dist/kernel-selector.js +1 -1
  52. package/dist/module-hooks/iframe-blocker.js +1 -1
  53. package/dist/postcss-tailwind3.js +1 -0
  54. package/dist/provider.js +1 -1
  55. package/dist/server/fetch-ssr-data.js +1 -1
  56. package/dist/server/headers.js +1 -1
  57. package/dist/server/normalize-url.js +1 -1
  58. package/dist/styles.tw3.css +16 -16
  59. package/dist/transports/offline.js +1 -1
  60. package/dist/utils/merge-slot-props.js +1 -1
  61. package/dist/utils/theme-utils.js +1 -1
  62. package/dist/version.js +1 -1
  63. package/dist-types/component-hooks/use-headless-consent-ui.d.ts +1 -1
  64. package/dist-types/components/iab-panel/atoms/feature-item.d.ts +28 -0
  65. package/dist-types/components/iab-panel/iab-panel.d.ts +2 -2
  66. package/dist-types/components/iab-panel/index.d.ts +5 -2
  67. package/dist-types/components/iab-panel/use-iab-translations.d.ts +4 -0
  68. package/dist-types/components/iab-prompt/iab-prompt.d.ts +2 -2
  69. package/dist-types/components/iab-prompt/index.d.ts +2 -2
  70. package/dist-types/components/panel/atoms/card.d.ts +1 -0
  71. package/dist-types/components/prompt/components.d.ts +6 -0
  72. package/dist-types/components/shared/libs/compose-refs.d.ts +20 -0
  73. package/dist-types/components/shared/ui/icon.d.ts +2 -1
  74. package/dist-types/context/consent-tracking-context.d.ts +10 -0
  75. package/dist-types/context/theme-context.d.ts +2 -2
  76. package/dist-types/context.d.ts +6 -0
  77. package/dist-types/devtools.d.ts +13 -2
  78. package/dist-types/draft-context.d.ts +31 -0
  79. package/dist-types/draft.d.ts +13 -30
  80. package/dist-types/gpp.d.ts +27 -0
  81. package/dist-types/hooks/use-focus-trap.d.ts +3 -1
  82. package/dist-types/hooks.d.ts +57 -13
  83. package/dist-types/iab.d.ts +1 -1
  84. package/dist-types/index-parts/consent-banner.d.ts +7 -0
  85. package/dist-types/index-parts/consent-dialog-link.d.ts +7 -0
  86. package/dist-types/index-parts/consent-dialog-trigger.d.ts +7 -0
  87. package/dist-types/index-parts/consent-dialog.d.ts +7 -0
  88. package/dist-types/index-parts/consent-gate.d.ts +7 -0
  89. package/dist-types/index-parts/core.d.ts +8 -0
  90. package/dist-types/index-parts/draft.d.ts +7 -0
  91. package/dist-types/index-parts/hooks.d.ts +7 -0
  92. package/dist-types/index-parts/module-hooks.d.ts +7 -0
  93. package/dist-types/index-parts/provider.d.ts +7 -0
  94. package/dist-types/index-parts/theme.d.ts +8 -0
  95. package/dist-types/index-parts/translations.d.ts +7 -0
  96. package/dist-types/index.d.ts +14 -15
  97. package/dist-types/kernel-selector.d.ts +0 -8
  98. package/dist-types/module-hooks/iframe-blocker.d.ts +12 -7
  99. package/dist-types/postcss-tailwind3.d.ts +21 -0
  100. package/dist-types/provider.d.ts +57 -17
  101. package/dist-types/server/headers.d.ts +14 -4
  102. package/dist-types/server/normalize-url.d.ts +14 -7
  103. package/dist-types/server/types.d.ts +12 -1
  104. package/dist-types/transports/offline.d.ts +7 -16
  105. package/dist-types/ui-config-context.d.ts +7 -1
  106. package/dist-types/utils/merge-slot-props.d.ts +15 -1
  107. package/dist-types/utils/theme-utils.d.ts +17 -2
  108. package/dist-types/version.d.ts +1 -1
  109. package/docs/README.md +92 -72
  110. package/docs/assets/v3/bottom-bar.png +0 -0
  111. package/docs/assets/v3/brand-card.png +0 -0
  112. package/docs/assets/v3/brand-preferences.png +0 -0
  113. package/docs/assets/v3/choice-wall.png +0 -0
  114. package/docs/assets/v3/headless-bar-html.png +0 -0
  115. package/docs/assets/v3/headless-bar-mobile.png +0 -0
  116. package/docs/assets/v3/headless-bar.png +0 -0
  117. package/docs/assets/v3/slim-bar.png +0 -0
  118. package/docs/concepts/choose-your-setup.md +87 -0
  119. package/docs/{frameworks/react/concepts → concepts}/consent-categories.md +22 -7
  120. package/docs/{guides → concepts}/consent-state.md +89 -105
  121. package/docs/{guides → concepts}/data-fetching.md +31 -27
  122. package/docs/concepts/how-consent-works.md +123 -0
  123. package/docs/{frameworks/react/concepts/policy-presets.md → concepts/policies.md} +7 -59
  124. package/docs/customization/class-names.md +202 -0
  125. package/docs/customization/dark-mode.md +157 -0
  126. package/docs/customization/motion.md +119 -0
  127. package/docs/customization/overview.md +67 -34
  128. package/docs/customization/recipes.md +839 -49
  129. package/docs/customization/slots.md +216 -35
  130. package/docs/customization/stylesheets.md +147 -0
  131. package/docs/customization/tailwind.md +842 -0
  132. package/docs/customization/tokens.md +163 -96
  133. package/docs/customization/translations.md +60 -3
  134. package/docs/frameworks/react/callbacks.md +118 -0
  135. package/docs/frameworks/react/clear-on-revocation.md +106 -0
  136. package/docs/frameworks/react/components/consent-banner.md +42 -28
  137. package/docs/frameworks/react/components/consent-dialog-link.md +13 -11
  138. package/docs/frameworks/react/components/consent-dialog-trigger.md +81 -76
  139. package/docs/frameworks/react/components/consent-dialog.md +56 -50
  140. package/docs/frameworks/react/components/consent-gate.md +37 -20
  141. package/docs/frameworks/react/components/consent-provider.md +130 -0
  142. package/docs/frameworks/react/components/consent-widget.md +23 -20
  143. package/docs/frameworks/react/components/dev-tools.md +53 -85
  144. package/docs/frameworks/react/components.md +105 -0
  145. package/docs/frameworks/react/compose.md +354 -0
  146. package/docs/frameworks/react/content-security-policy.md +126 -0
  147. package/docs/frameworks/react/customize.md +250 -0
  148. package/docs/frameworks/react/embeds.md +89 -0
  149. package/docs/frameworks/react/gpp.md +163 -0
  150. package/docs/frameworks/react/{hooks/overview.md → hooks.md} +21 -16
  151. package/docs/frameworks/react/{iab/overview.md → iab.md} +80 -31
  152. package/docs/frameworks/react/network-blocker.md +35 -41
  153. package/docs/frameworks/react/quickstart.md +139 -101
  154. package/docs/frameworks/react/rendering.md +128 -0
  155. package/docs/frameworks/react/scripts.md +115 -0
  156. package/docs/frameworks/react/translations.md +155 -0
  157. package/docs/frameworks/react/troubleshooting.md +92 -47
  158. package/docs/frameworks/react/vendor-consent.md +170 -0
  159. package/docs/guides/banner-experiments.md +654 -0
  160. package/docs/guides/troubleshooting.md +120 -47
  161. package/docs/guides/verify-consent.md +81 -49
  162. package/docs/integrations/adobe-analytics.md +167 -159
  163. package/docs/integrations/ahrefs-analytics.md +152 -154
  164. package/docs/integrations/amplitude.md +162 -156
  165. package/docs/integrations/building-integrations.md +136 -37
  166. package/docs/integrations/clearbit.md +154 -154
  167. package/docs/integrations/cloudflare-web-analytics.md +156 -156
  168. package/docs/integrations/cloudflare-zaraz.md +209 -261
  169. package/docs/integrations/crisp.md +164 -158
  170. package/docs/integrations/databuddy.md +157 -173
  171. package/docs/integrations/fathom-analytics.md +158 -156
  172. package/docs/integrations/front-chat.md +167 -167
  173. package/docs/integrations/google-maps.md +118 -83
  174. package/docs/integrations/google-tag-manager.md +178 -163
  175. package/docs/integrations/google-tag.md +163 -160
  176. package/docs/integrations/heap.md +163 -155
  177. package/docs/integrations/hightouch.md +161 -157
  178. package/docs/integrations/hotjar.md +159 -155
  179. package/docs/integrations/intercom.md +183 -153
  180. package/docs/integrations/klaviyo.md +486 -0
  181. package/docs/integrations/linkedin-insights.md +174 -150
  182. package/docs/integrations/logrocket.md +160 -156
  183. package/docs/integrations/matomo-analytics.md +188 -178
  184. package/docs/integrations/meta-pixel.md +188 -150
  185. package/docs/integrations/microsoft-clarity.md +163 -155
  186. package/docs/integrations/microsoft-uet.md +148 -154
  187. package/docs/integrations/mixpanel-analytics.md +155 -160
  188. package/docs/integrations/one-dollar-stats.md +166 -165
  189. package/docs/integrations/openai-pixel.md +204 -301
  190. package/docs/integrations/overview.md +137 -80
  191. package/docs/integrations/pinterest-tag.md +191 -183
  192. package/docs/integrations/pirsch.md +169 -159
  193. package/docs/integrations/plausible-analytics.md +172 -158
  194. package/docs/integrations/posthog.md +313 -240
  195. package/docs/integrations/promptwatch.md +154 -154
  196. package/docs/integrations/reddit-pixel.md +185 -157
  197. package/docs/integrations/rudderstack.md +201 -186
  198. package/docs/integrations/rybbit-analytics.md +171 -160
  199. package/docs/integrations/segment.md +182 -154
  200. package/docs/integrations/snapchat-pixel.md +186 -156
  201. package/docs/integrations/tiktok-pixel.md +171 -150
  202. package/docs/integrations/umami-analytics.md +163 -158
  203. package/docs/integrations/vercel-analytics.md +166 -157
  204. package/docs/integrations/x-pixel.md +176 -150
  205. package/docs/integrations/youtube.md +121 -86
  206. package/docs/upgrade-v3.md +496 -467
  207. package/package.json +20 -9
  208. package/dist/ui-save.js +0 -1
  209. package/dist-types/ui-save.d.ts +0 -25
  210. package/docs/assets/v3/brand-bar.png +0 -0
  211. package/docs/assets/v3/mobile-card.png +0 -0
  212. package/docs/assets/v3/preferences.png +0 -0
  213. package/docs/frameworks/react/components/consent-manager-provider.md +0 -95
  214. package/docs/frameworks/react/script-loader.md +0 -69
  215. package/docs/frameworks/react/styling/overview.md +0 -266
  216. package/docs/guides/deployment-modes.md +0 -75
  217. package/docs/guides/shared-consent-controls.md +0 -158
  218. package/docs/integrations/clear-on-revocation.md +0 -167
  219. package/docs/integrations/granular-consent.md +0 -210
@@ -0,0 +1,130 @@
1
+ ---
2
+ title: ConsentProvider
3
+ description: Mount ConsentProvider in a React app and configure its backend,
4
+ scripts, storage, blocking and presentation options.
5
+ group: frameworks
6
+ ---
7
+
8
+ ## Mount one provider
9
+
10
+ `ConsentProvider` creates the consent runtime, loads the policy, stores the
11
+ visitor's choice and loads consent-gated scripts. Render one provider at the
12
+ root of your app and put the banner, dialog and a preferences link inside it.
13
+ The [quickstart](../quickstart.md) does this in
14
+ `src/consent.tsx`:
15
+
16
+ ```tsx title="src/consent.tsx"
17
+ import {
18
+ ConsentBanner,
19
+ ConsentDialog,
20
+ ConsentDialogLink,
21
+ ConsentProvider,
22
+ hosted,
23
+ } from 'c15t/react';
24
+ import type { ReactNode } from 'react';
25
+
26
+ import { scripts } from './scripts';
27
+
28
+ import 'c15t/react/styles.css';
29
+
30
+ const mode = hosted({ url: 'https://your-project.inth.app' });
31
+
32
+ export const Consent = ({ children }: { children: ReactNode }) => (
33
+ <ConsentProvider options={{ mode, scripts }}>
34
+ {children}
35
+ <ConsentBanner />
36
+ <ConsentDialog />
37
+ <footer>
38
+ <ConsentDialogLink>Privacy settings</ConsentDialogLink>
39
+ </footer>
40
+ </ConsentProvider>
41
+ );
42
+ ```
43
+
44
+ `hosted({ url })` points the provider at your Inth backend. Replace
45
+ `https://your-project.inth.app` with the URL from your Inth project. The
46
+ Next.js and TanStack Start adapters export `ConsentRoot` instead, which wraps
47
+ this provider and passes it server-resolved state.
48
+
49
+ ## Provider options
50
+
51
+ Keep the provider mounted across client navigation. It reads `mode`,
52
+ `prefetch`, `persistence`, `storageConfig`, `clearOnRevocation`, `i18n` and
53
+ `experiment` once, when it mounts. Outside production, changing `mode`,
54
+ `i18n`, `experiment`, `persistence` or `storageConfig` logs a warning. Remount
55
+ the provider to change them. `scripts`, `vendors`, `networkBlocker`,
56
+ `iframeBlocker`, `consentCategories`, `overrides`, `user`, `callbacks` and
57
+ `enabled` apply when they change.
58
+
59
+ | Option | Purpose |
60
+ | ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
61
+ | `mode` | Required. `hosted({ url })` for a backend, `offline()` for local policies, or `custom()` for your own transport |
62
+ | `prefetch` | Server-resolved state, or a promise of it, from a framework adapter's server helper |
63
+ | `scripts` | Consent-gated vendor scripts. See [scripts and embeds](../scripts.md) |
64
+ | `vendors` | Vendors listed under their category with their own switch. See [vendor consent](../vendor-consent.md) |
65
+ | `scriptLoader` | Options for the script loader module |
66
+ | `networkBlocker` | Rules that hold `fetch` and `XMLHttpRequest` calls until their category is allowed, or `false` |
67
+ | `iframeBlocker` | Gates DOM iframes that carry `data-category`. On by default; `false` turns it off |
68
+ | `persistence` | Stores the visitor's choice in a cookie and localStorage. On by default |
69
+ | `storageConfig` | Storage key and cookie settings for the stored choice |
70
+ | `clearOnRevocation` | Cookies and storage keys to delete when a category is denied |
71
+ | `reloadOnConsentRevoked` | Reload the page after the visitor turns off a granted category or vendor. Defaults to `true` |
72
+ | `consentCategories` | Categories the preference dialog offers alongside those your scripts use, within the policy's scope |
73
+ | `overrides` | Force the country, region or language the policy is resolved for, for testing |
74
+ | `user` | Link consent records to a signed-in user |
75
+ | `callbacks` | Choice, permission and error events |
76
+ | `i18n` | Languages and message overrides |
77
+ | `legalLinks` | Privacy policy and other links shown in the banner and dialog |
78
+ | `theme` | `consentActions` and slot styles. Tokens go through `ConsentTheme`; see [customize](../customize.md) |
79
+ | `components` | Slot attributes per component part |
80
+ | `presentation` | Banner and dialog shape, position and blocking |
81
+ | `experiment` | A/B test of `presentation`, read once at mount. See [banner experiments](../../../guides/banner-experiments.md) |
82
+ | `colorScheme` | `'light'`, `'dark'`, `'system'`, or `null` to leave the `c15t-dark` class alone |
83
+ | `noStyle`, `disableAnimation` | Drop the built-in styles, or turn off animations |
84
+ | `preloadDialog` | When the deferred `ConsentDialog` starts loading: `'idle'` (default) or `'intent'`; see [ConsentDialog](./consent-dialog.md#behavior) |
85
+ | `nonce` | Content Security Policy nonce for script elements c15t inserts |
86
+ | `enabled` | Set `false` only when you deliberately bypass consent enforcement |
87
+
88
+ `enabled: false` grants categories and allows gated loading while suppressing
89
+ consent UI. It is not a way to fix failed initialization or hide a banner in
90
+ production.
91
+
92
+ ## Observe changes without inventing choices
93
+
94
+ `onChoiceRecorded` reports explicit accept, reject and save actions.
95
+ `onPermissionsChanged` also covers changes caused by policy, expiry or privacy
96
+ signals. Hydration and notice dismissal do not become explicit choices.
97
+
98
+ An externally owned runtime can be passed through `runtime`. Its owner must
99
+ start and dispose it. A provider borrowing that runtime must not initialize or
100
+ dispose a second copy. Passing a different `runtime` moves the provider and its
101
+ hooks to the new runtime's kernel and unsubscribes them from the previous one.
102
+ The previous runtime keeps running until its owner disposes it. Switch the
103
+ provider to another runtime, or unmount it, before disposing the runtime it
104
+ renders. Changing between a borrowed runtime and a provider-created one still
105
+ requires remounting the provider. IAB actions taken before its handle is ready
106
+ wait for that handle. If the provider unmounts or switches runtimes first, a
107
+ pending `save()` rejects with an `AbortError` instead of waiting indefinitely.
108
+ See [how consent works](../../../concepts/how-consent-works.md#a-permission-is-not-a-recorded-choice) and
109
+ [choose your setup](../../../concepts/choose-your-setup.md).
110
+
111
+ ## Reload after revocation
112
+
113
+ Removing a script cannot stop code that already ran. A vendor's listeners,
114
+ timers and widgets keep working until the page unloads. When an accept, reject
115
+ or save turns off a category or vendor that was granted, the provider reloads
116
+ the page once the save request settles, so the next page runs only permitted
117
+ code. `onBeforeConsentRevocationReload` runs just before the reload.
118
+
119
+ Otherwise, expiry, policy changes and privacy signals do not reload the page. Set
120
+ `reloadOnConsentRevoked: false` only when every gated vendor stops itself on
121
+ revocation, for example through its own opt-out API.
122
+
123
+ ## Clear data when consent is denied
124
+
125
+ Set `options.clearOnRevocation` to declare cookies, localStorage keys, and
126
+ sessionStorage keys by optional consent category. Cleanup waits for policy
127
+ resolution, then clears denied categories and later allowed-to-denied
128
+ transitions. This option is initial-only. See
129
+ [clear on revocation](../clear-on-revocation.md) for examples,
130
+ cookie scopes, and browser limits.
@@ -8,7 +8,7 @@ group: frameworks
8
8
  ## Embed the preference center inline
9
9
 
10
10
  `ConsentWidget` renders the preference center in the page flow instead of in
11
- a modal: the category switches and the Reject all, Accept all and Save
11
+ a modal: the category switches and the Reject All, Accept All and Save Settings
12
12
  actions, without a title, backdrop or portal. Use it on a privacy or
13
13
  settings page where the visitor expects the controls to be part of the
14
14
  content. Render it anywhere inside the `ConsentProvider` from your
@@ -19,16 +19,16 @@ banner's Customize button and preference links still have a dialog to open.
19
19
  import { ConsentWidget } from 'c15t/react';
20
20
 
21
21
  export function PrivacyPage() {
22
- return (
23
- <main>
24
- <h1>Privacy</h1>
25
- <p>Choose which optional categories this site may use.</p>
26
- <section aria-labelledby="cookie-preferences">
27
- <h2 id="cookie-preferences">Cookie preferences</h2>
28
- <ConsentWidget />
29
- </section>
30
- </main>
31
- );
22
+ return (
23
+ <main>
24
+ <h1>Privacy</h1>
25
+ <p>Choose which optional categories this site may use.</p>
26
+ <section aria-labelledby="cookie-preferences">
27
+ <h2 id="cookie-preferences">Cookie preferences</h2>
28
+ <ConsentWidget />
29
+ </section>
30
+ </main>
31
+ );
32
32
  }
33
33
  ```
34
34
 
@@ -49,13 +49,16 @@ or dialog by itself.
49
49
 
50
50
  The widget is the preference center without the modal around it: an
51
51
  accordion with one row per category, each with a switch, followed by the
52
- Reject all, Accept all and Save actions. `ConsentDialog` renders this same
52
+ Reject All, Accept All and Save Settings actions. `ConsentDialog` renders this same
53
53
  widget inside its card. Standalone, the widget renders in place, takes part
54
54
  in server rendering, and does not touch the active surface, so it suits a
55
55
  privacy page or an account settings screen.
56
56
 
57
57
  The rows are `necessary` plus the categories in the active policy's scope,
58
- narrowed to the provider `options.consentCategories` when that list is non-empty.
58
+ narrowed to the categories the provider declares: `options.consentCategories`
59
+ and the categories of its scripts, network rules, vendors and discovered
60
+ frames. When nothing is declared, a permissive policy shows only `necessary`
61
+ and a strict one its whole scope.
59
62
  The `necessary` switch is on and disabled. Row titles and descriptions come
60
63
  from `consentTypes.<category>.title` and `.description`; the action labels
61
64
  from `common.rejectAll`, `common.acceptAll` and `common.save`. One row is
@@ -68,8 +71,8 @@ until Save. The draft seeds each category from the recorded choice; without
68
71
  one it uses `presentation.preferences.defaults` from the provider options,
69
72
  and without that it is on under an opt-out rule and on only for the rule's
70
73
  preselected categories under an opt-in rule. Save records the displayed
71
- categories, then reseeds the draft from the new record. Accept all and
72
- Reject all record immediately without a separate Save. A save answers a
74
+ categories, then reseeds the draft from the new record. Accept All and
75
+ Reject All record immediately without a separate Save. A save answers a
73
76
  choice prompt, so an open choice banner closes; it does not dismiss a
74
77
  `notice` prompt, which keeps its own acknowledgement record. Unsaved toggles
75
78
  are lost when the widget unmounts.
@@ -102,18 +105,18 @@ only; the category stays granted and its other vendors keep loading. While
102
105
  the category is off in the draft the vendor switches are disabled, with the
103
106
  hint "Turn on this category to choose vendors." A vendor declared
104
107
  `disabled`, or one that only falls under `necessary`, is listed without a
105
- switch. Accept all and Reject all clear every vendor denial, including one
108
+ switch. Accept All and Reject All clear every vendor denial, including one
106
109
  staged but not yet saved. Under an `iab` policy no vendor rows render.
107
110
  Vendors that are only referenced by a script slug, without a name and
108
111
  privacy policy URL, still gate loading but are not listed, and so does a
109
112
  vendor whose condition negates a category, since no category row can
110
113
  host it. See
111
- [granular consent](../../../integrations/granular-consent.md).
114
+ [vendor consent](../vendor-consent.md).
112
115
 
113
116
  ### Actions
114
117
 
115
- The preference center always renders Reject all, Accept all and Save, with
116
- Save as the primary action. `presentation.preferences.layout`,
118
+ The preference center always renders Reject All, Accept All and Save Settings, with
119
+ Save Settings as the primary action. `presentation.preferences.layout`,
117
120
  `primaryActions`, `direction` and `uiProfile` in the provider options
118
121
  reorder and restyle them; a layout that omits one of the three has it
119
122
  restored. Customize and dismiss actions never render here.
@@ -201,7 +204,7 @@ it. `c15t/react/components/consent-widget` exports the widget without that
201
204
  deferral, plus each part as a named export.
202
205
 
203
206
  The compound parts import from `c15t/react` as properties of `ConsentWidget`.
204
- See [Styling](../styling/overview.md) for slots and tokens.
207
+ See [customize](../customize.md) for slots and tokens.
205
208
 
206
209
  ## Verify
207
210
 
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  title: DevTools
3
- description: A development tool for inspecting consent state, geolocation,
4
- loaded scripts, and consent events in real time.
3
+ description: Inspect consent state, location, loaded scripts and consent events
4
+ in a React app during development with the c15t DevTools panel.
5
5
  group: frameworks
6
6
  ---
7
7
 
@@ -12,42 +12,44 @@ group: frameworks
12
12
 
13
13
  ## Installation
14
14
 
15
- No extra package is required for a framework adapter. The adapter and its
16
- engine are included with the `c15t` package and the dedicated
17
- `@c15t/svelte` adapter. Install `@c15t/dev-tools` directly only
18
- when you need the imperative `createDevTools({ kernel })` API.
15
+ No extra package is required. The React adapter and its engine are included
16
+ with the `c15t` package. Install `@c15t/dev-tools` directly only when you
17
+ need the imperative `createDevTools({ kernel })` API.
19
18
 
20
- ## Usage
19
+ ## Render DevTools in development builds
21
20
 
22
- This example uses Vite's `import.meta.env.DEV` build-time flag. For another
23
- bundler, use its development flag instead. For example, webpack projects can
24
- use a configured `process.env.NODE_ENV === 'development'` replacement;
25
- `process` is not a browser global.
21
+ Render `DevTools` inside `ConsentProvider`. This example loads it only in
22
+ development with Vite's `import.meta.env.DEV` flag, so production bundles never
23
+ download it. For another bundler, use its development flag instead; for
24
+ example, webpack projects can use a configured
25
+ `process.env.NODE_ENV === 'development'` replacement, since `process` is not a
26
+ browser global.
26
27
 
27
- ```tsx
28
- import { lazy, Suspense, type ReactNode } from 'react';
29
- import { ConsentProvider, hosted } from 'c15t/react';
30
-
31
- const DevTools =
32
- import.meta.env.DEV
33
- ? lazy(() =>
34
- import('c15t/react/devtools').then(({ DevTools }) => ({ default: DevTools })),
35
- )
36
- : () => null;
28
+ ```tsx title="src/consent-devtools.tsx"
29
+ import { lazy, Suspense } from 'react';
37
30
 
38
- export function ConsentManager({ children }: { children: ReactNode }) {
39
- return (
40
- <ConsentProvider options={{ mode: hosted({ url: 'https://your-instance.c15t.dev' }) }}>
41
- {children}
42
- <Suspense fallback={null}>
43
- <DevTools />
44
- </Suspense>
45
- </ConsentProvider>
46
- );
31
+ const DevTools = import.meta.env.DEV
32
+ ? lazy(() =>
33
+ import('c15t/react/devtools').then(({ DevTools }) => ({
34
+ default: DevTools,
35
+ }))
36
+ )
37
+ : null;
38
+
39
+ export function ConsentDevTools() {
40
+ return DevTools ? (
41
+ <Suspense fallback={null}>
42
+ <DevTools />
43
+ </Suspense>
44
+ ) : null;
47
45
  }
48
46
  ```
49
47
 
50
- Import the adapter from `c15t/react/devtools` when you use the umbrella package, or `c15t/react/devtools` when you install the React package directly. `@c15t/dev-tools` exports the imperative engine, not the React component.
48
+ Render `<ConsentDevTools />` inside the provider from your
49
+ [quickstart](../quickstart.md), next to the banner. Import the
50
+ component from `c15t/react/devtools` when you installed the `c15t` package, or
51
+ from `@c15t/react/devtools` when you installed `@c15t/react` directly.
52
+ `@c15t/dev-tools` exports the imperative engine, not the React component.
51
53
 
52
54
  If the CLI cannot identify your bundler's development flag, it generates
53
55
  `const DevTools = false` with a setup comment. Replace `false` with your
@@ -69,14 +71,16 @@ DevTools disabled instead of assuming browser globals or loading it in productio
69
71
  `position` accepts any corner: `top-left`, `top-right`, `bottom-left`, or
70
72
  `bottom-right`. Set `defaultOpen` to open the initial panel and `defaultTab`
71
73
  to choose that panel. `maxEvents` limits captured kernel and script events.
72
- Set `disabled` to prevent mounting without removing the component.
74
+ Set `disabled` to prevent mounting without removing the component. The
75
+ panel renders inside a shadow root by default, so page CSS does not reach
76
+ it. `shadow={false}` mounts it in the light DOM with its stylesheet in
77
+ `<head>`.
73
78
 
74
79
  ## Panels
75
80
 
76
81
  Accept and reject apply only to categories displayed by the provider, leaving
77
- hidden consent values unchanged. `necessary` always remains enabled. Svelte
78
- and Vue adapters use their provider's displayed categories; the kernel and
79
- React adapter use the current policy categories by default.
82
+ hidden consent values unchanged. `necessary` always remains enabled. The
83
+ React adapter uses the current policy categories by default.
80
84
 
81
85
  For an imperative integration with a narrower UI, pass
82
86
  `getConsentCategories: () => ['necessary', 'measurement']` to
@@ -91,7 +95,7 @@ explicit scope or policy categories, bulk saves cover all known categories.
91
95
  | ------------ | -------------------------------------------------------------------------------------------- |
92
96
  | **Consents** | Inspect and save categories, accept all, or reject optional categories |
93
97
  | **Scripts** | Configured scripts, loading status, search, and external page resources |
94
- | **Location** | Detected jurisdiction, country, region, and consent model |
98
+ | **Location** | Resolved country and region, and location overrides |
95
99
  | **Policy** | Resolved policy and UI configuration |
96
100
  | **IAB** | Edit vendors, purposes, legitimate interests, and special features; save and copy TC strings |
97
101
  | **Events** | Timeline of consent changes, kernel events, and script lifecycle events |
@@ -168,55 +172,6 @@ For custom inspection tools, `getIABControls(kernel)` and
168
172
  The getter returns undefined before module initialization and after disposal.
169
173
  A snapshot without an attached IAB module is read-only in DevTools.
170
174
 
171
- ## Vue and Svelte
172
-
173
- Vue imports `ConsentDevTools` from `c15t/vue/devtools`.
174
- Render it in the app where the c15t plugin provides
175
- the kernel. In Nuxt, the configured c15t module provides that context.
176
-
177
- ```vue
178
- <script setup lang="ts">
179
- import { defineAsyncComponent } from 'vue';
180
-
181
- const DevTools = import.meta.env.DEV
182
- ? defineAsyncComponent(() => import('c15t/vue/devtools'))
183
- : null;
184
- </script>
185
-
186
- <template>
187
- <DevTools v-if="DevTools" position="bottom-right" />
188
- </template>
189
- ```
190
-
191
- In Nuxt, use `import.meta.dev` instead of `import.meta.env.DEV`.
192
- Svelte imports the adapter from `@c15t/svelte/devtools` and renders it
193
- inside `ConsentManagerProvider`.
194
-
195
- ```svelte
196
- <script lang="ts">
197
- import { ConsentManagerProvider, offline } from '@c15t/svelte';
198
-
199
- const devTools = import.meta.env.DEV
200
- ? import('@c15t/svelte/devtools')
201
- : null;
202
- </script>
203
-
204
- <ConsentManagerProvider options={{ mode: offline() }}>
205
- {#if devTools}
206
- {#await devTools then { ConsentDevTools }}
207
- <ConsentDevTools position="bottom-right" />
208
- {/await}
209
- {/if}
210
- </ConsentManagerProvider>
211
- ```
212
-
213
- Both adapters accept `position`, `defaultOpen`, `defaultTab`, `maxEvents`,
214
- `shadow` (`false` renders into the page instead of a shadow root),
215
- and `getConsentCategories`. Changing these presentation options recreates
216
- the panel and clears its event history. They do not accept React's `disabled`
217
- prop; conditionally render them instead. Unmounting removes the panel and
218
- subscriptions without changing consent.
219
-
220
175
  ## TanStack Devtools
221
176
 
222
177
  Each embedded panel owns its event history and subscriptions. Unmounting it
@@ -257,6 +212,19 @@ export function AppDevtools() {
257
212
 
258
213
  In a Next.js app, import `c15tDevtools` from `c15t/next/devtools` instead. Scoped installs use `@c15t/react/devtools` and `@c15t/nextjs/devtools`.
259
214
 
215
+ The c15t panel follows the TanStack Devtools light or dark theme, not your app's consent theme. If you write the plugin entry yourself, use a render function and pass the theme to `C15tTanStackDevtoolsPanel`:
216
+
217
+ ```tsx
218
+ import { C15tTanStackDevtoolsPanel } from 'c15t/react/devtools';
219
+
220
+ const c15tPlugin = {
221
+ name: 'Consent',
222
+ render: (_element: HTMLElement, { theme }: { theme: 'light' | 'dark' }) => (
223
+ <C15tTanStackDevtoolsPanel theme={theme} />
224
+ ),
225
+ };
226
+ ```
227
+
260
228
  ## Props
261
229
 
262
230
  | Property | Type | Description | Default | Required |
@@ -0,0 +1,105 @@
1
+ ---
2
+ title: Components
3
+ description: Every c15t component a React app imports from c15t/react, what each
4
+ renders, where each one mounts, and which to start with.
5
+ group: frameworks
6
+ ---
7
+
8
+ ## Component reference
9
+
10
+ `c15t/react` exports the provider, the consent UI and the hooks from one
11
+ import path. Every component except `ConsentProvider` and `ConsentTheme`
12
+ reads the runtime the provider creates, so render them inside
13
+ `ConsentProvider`.
14
+
15
+ | Component | Renders | Reference |
16
+ | ------------------------------------- | ----------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------- |
17
+ | `ConsentProvider` | The consent runtime, which loads the policy, stores the choice and loads scripts. No markup of its own | [ConsentProvider](./components/consent-provider.md) |
18
+ | `ConsentBanner` | The banner, when the visitor's policy asks for one | [ConsentBanner](./components/consent-banner.md) |
19
+ | `ConsentDialog` | The preference center as a modal | [ConsentDialog](./components/consent-dialog.md) |
20
+ | `ConsentWidget` | The preference center inline in a page, for a privacy route | [ConsentWidget](./components/consent-widget.md) |
21
+ | `ConsentDialogLink` | An unstyled button that opens the dialog, for your footer | [ConsentDialogLink](./components/consent-dialog-link.md) |
22
+ | `ConsentDialogTrigger` | A floating, draggable button that opens the dialog. `ConsentDialogTriggerToolbar` adds your own actions beside it | [ConsentDialogTrigger](./components/consent-dialog-trigger.md) |
23
+ | `ConsentGate` | Its children while a category is allowed, a placeholder otherwise | [ConsentGate](./components/consent-gate.md) |
24
+ | `ConsentTheme` | A `<style>` element with your theme tokens | [Customize](./customize.md) |
25
+ | `DevTools` from `c15t/react/devtools` | A development panel for consent state, scripts and events | [DevTools](./components/dev-tools.md) |
26
+
27
+ Mount one `ConsentProvider` at the root of the app and keep it mounted across
28
+ client navigation. The provider reads `mode` and its module options once,
29
+ when it mounts.
30
+
31
+ ## Start with the provider, banner and dialog
32
+
33
+ Most apps start with four components. `ConsentProvider` holds the runtime,
34
+ `ConsentBanner` and `ConsentDialog` ask for a choice, and a
35
+ `ConsentDialogLink` lets visitors reopen their preferences. The [quickstart](./quickstart.md) puts them in
36
+ one file:
37
+
38
+ ```tsx title="src/consent.tsx"
39
+ import {
40
+ ConsentBanner,
41
+ ConsentDialog,
42
+ ConsentDialogLink,
43
+ ConsentProvider,
44
+ hosted,
45
+ } from 'c15t/react';
46
+ import type { ReactNode } from 'react';
47
+
48
+ import { scripts } from './scripts';
49
+
50
+ import 'c15t/react/styles.css';
51
+
52
+ const mode = hosted({ url: 'https://your-project.inth.app' });
53
+
54
+ export const Consent = ({ children }: { children: ReactNode }) => (
55
+ <ConsentProvider options={{ mode, scripts }}>
56
+ {children}
57
+ <ConsentBanner />
58
+ <ConsentDialog />
59
+ <footer>
60
+ <ConsentDialogLink>Privacy settings</ConsentDialogLink>
61
+ </footer>
62
+ </ConsentProvider>
63
+ );
64
+ ```
65
+
66
+ Add `ConsentWidget`, `ConsentDialogTrigger` or `ConsentGate` later, where a
67
+ page needs them. Keep `ConsentDialog` mounted even when you add a widget,
68
+ because the banner's Customize button and every dialog link open it.
69
+
70
+ ## Server-rendered React apps
71
+
72
+ React Router framework mode, Remix and other server-rendered React apps use the
73
+ same components. `ConsentProvider` renders on the server without touching
74
+ browser APIs, and the banner mounts after hydration, once the browser has
75
+ resolved the policy. [Rendering](./rendering.md#react-router-remix-and-other-server-rendered-apps)
76
+ explains the trade-off.
77
+
78
+ ## IAB TCF components
79
+
80
+ Import `IABProvider`, `IABConsentBanner` and `IABConsentDialog` from
81
+ `c15t/react/iab`, render them inside `ConsentProvider` next to
82
+ `ConsentBanner` and `ConsentDialog`, and load `c15t/react/iab/styles.css`. The stock banner and dialog stay closed
83
+ under an IAB policy. See
84
+ [IAB TCF](./iab.md).
85
+
86
+ ## Other entry points
87
+
88
+ | Import | Exports | Use |
89
+ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------ | -------------------------------------------------------------------------------------------------------- |
90
+ | `c15t/react/consent-banner`, `c15t/react/consent-dialog`, `c15t/react/consent-widget`, `c15t/react/consent-dialog-link`, `c15t/react/consent-dialog-trigger`, `c15t/react/consent-gate` | One component each | A module that needs one component without the rest of the adapter |
91
+ | `c15t/react/headless` | Headless hooks | Your own banner and dialog markup. See [headless](./headless.md) |
92
+ | `c15t/react/server` | `fetchSSRData` and its helpers | Resolve `/init` in a server loader. See [rendering](./rendering.md#skip-the-browsers-first-init-request) |
93
+ | `c15t/react/devtools` | `DevTools` | Development only |
94
+
95
+ ## Check the components
96
+
97
+ Load the app in a private window under a policy that asks for a choice:
98
+
99
+ 1. The banner shows, and DevTools Network has no requests to your vendors'
100
+ hosts.
101
+ 2. Click the footer's `ConsentDialogLink`. The dialog opens with every optional
102
+ category off.
103
+ 3. Allow one category and save. Its vendor requests appear, and a
104
+ `ConsentGate` for that category mounts its children.
105
+ 4. Reload. The banner stays closed, and the link still reopens the dialog.