@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,170 @@
1
+ ---
2
+ title: Vendor consent
3
+ description: Let visitors allow a category such as marketing in a React app and
4
+ still turn off one vendor in it, with the vendors option on ConsentProvider
5
+ and useVendorAllowed.
6
+ group: frameworks
7
+ ---
8
+
9
+ ## How vendor consent works
10
+
11
+ A visitor allows marketing, then switches off X Pixel. Other marketing
12
+ scripts load; X Pixel does not. c15t loads a script, network request or
13
+ iframe that names a vendor only when both are true:
14
+
15
+ * Its category is allowed.
16
+ * The visitor has not switched its vendor off.
17
+
18
+ A vendor switch never grants a category. With marketing denied, X Pixel stays
19
+ blocked whatever its own switch says. You do not need IAB TCF for this. Under
20
+ an `iab` policy, c15t ignores vendor slugs and takes vendor consent from the
21
+ TC string instead; see [IAB TCF](./iab.md).
22
+
23
+ ## Declare the vendors
24
+
25
+ Declare the vendors next to the scripts in `src/scripts.ts`, using the `vendor`
26
+ slug each script already carries:
27
+
28
+ ```ts title="src/scripts.ts (partial)"
29
+ import type { Vendor } from 'c15t';
30
+
31
+ export const vendors: Vendor[] = [
32
+ {
33
+ id: 'x-pixel',
34
+ name: 'X Pixel',
35
+ category: 'marketing',
36
+ privacyPolicyUrl: 'https://x.com/privacy',
37
+ },
38
+ ];
39
+ ```
40
+
41
+ Then add them to the provider options in `src/consent.tsx`:
42
+
43
+ ```tsx title="src/consent.tsx (partial)"
44
+ import { scripts, vendors } from './scripts';
45
+
46
+ <ConsentProvider options={{ mode, scripts, vendors }}>
47
+ ```
48
+
49
+ `vendors` is a live option, like `scripts`: passing a new list replaces the
50
+ declared vendors. A provider that borrows a `runtime` does not accept
51
+ `vendors`; declare them in `createConsentRuntime({ vendors })` instead.
52
+
53
+ ## Vendor fields
54
+
55
+ | Field | Required | Behavior |
56
+ | ----------------------------------------- | -------- | --------------------------------------------------------------------------- |
57
+ | `id` | Yes | Lowercase slug of up to 64 characters: letters, digits, `.`, `_` and `-`. |
58
+ | `name` | Yes | Name shown in the preference dialog. |
59
+ | `category` | Yes | A category, or a condition such as `{ or: ['measurement', 'marketing'] }`. |
60
+ | `privacyPolicyUrl` | Yes | Link shown next to the vendor. |
61
+ | `description`, `legalName`, `homepageUrl` | No | Extra detail shown on the vendor card. |
62
+ | `disabled` | No | List the vendor without a switch. A stored denial for it no longer applies. |
63
+
64
+ The `id` must match the `vendor` slug on the script. Every `@c15t/integrations`
65
+ helper sets `vendor` to its script ID, so `xPixel()` is `x-pixel`, `gtag()`
66
+ is `gtag` and `cloudflareZaraz()` is `cloudflare-zaraz`. Each
67
+ [vendor guide](../../integrations/overview.md) names its slug.
68
+
69
+ A self-hosted backend can declare vendors too. `/init` returns them and c15t
70
+ merges them with the vendors in code. When both declare the same `id`, the
71
+ code declaration wins.
72
+
73
+ ## Gate scripts, requests and iframes by vendor
74
+
75
+ Put the vendor slug on each target:
76
+
77
+ | Target | Field |
78
+ | -------------------- | ----------------------------------------- |
79
+ | Script configuration | `vendor: 'x-pixel'` |
80
+ | Network blocker rule | `vendor: 'x-pixel'` |
81
+ | Iframe blocker | `data-vendor="x-pixel"` on the `<iframe>` |
82
+
83
+ An iframe with `data-vendor` and no `data-category` is gated on the vendor
84
+ alone. A script with `alwaysLoad` still loads when its vendor is off. Its
85
+ callbacks receive `info.vendor.granted`, so the SDK can stop itself. The
86
+ Google Consent Mode, RudderStack and Cloudflare Zaraz helpers do this for
87
+ you. While their vendor is off, they send every optional category as denied.
88
+
89
+ `ConsentGate` checks a category only. To render a component only while one
90
+ vendor is allowed, read `useVendorAllowed('youtube')` and render it when the
91
+ hook returns `true`. [Embeds](./embeds.md) covers the iframe
92
+ blocker's `data-vendor` attribute.
93
+
94
+ ## What the preference dialog shows
95
+
96
+ `ConsentDialog` and `ConsentWidget` list each declared vendor under its
97
+ category, with its own switch.
98
+
99
+ The dialog lists only vendors that have a `name` and a `privacyPolicyUrl`.
100
+ A vendor's switch is disabled while its category is off. Save records the
101
+ vendors the visitor changed. Accept All and Reject All clear every vendor
102
+ denial, so each vendor follows its category again. The labels come from
103
+ `consentManagerDialog.vendors` in the translations: `title`,
104
+ `privacyPolicy`, `disabledByCategory` and `switchLabel`.
105
+
106
+ ## Build your own vendor switch
107
+
108
+ `useVendorDraft()` from `c15t/react` reads and stages vendor switches on the
109
+ same draft the category switches use, so one save records both.
110
+ `useVendorAllowed(id)` returns whether a vendor may run now: it is declared,
111
+ its category is allowed and the visitor has not switched it off.
112
+
113
+ ```tsx title="src/x-pixel-switch.tsx (partial)"
114
+ import { useVendorAllowed, useVendorDraft } from 'c15t/react';
115
+
116
+ const { vendors, setVendor, save } = useVendorDraft();
117
+ const xPixelAllowed = useVendorAllowed('x-pixel');
118
+
119
+ <input
120
+ type="checkbox"
121
+ checked={vendors['x-pixel'] ?? true}
122
+ onChange={(event) => setVendor('x-pixel', event.target.checked)}
123
+ />
124
+ <button type="button" onClick={() => save()}>Save</button>
125
+ ```
126
+
127
+ `save()` resolves `{ ok: false }` without recording anything when the policy
128
+ or the vendor list changed under the edit. Call `reset()` to start again.
129
+ `useDeclaredVendors()` returns the declared vendors, and `useVendorChoice()`
130
+ returns the recorded vendor decision, or `null` before the visitor made one.
131
+
132
+ A vendor reads as allowed only when it is declared, its category is allowed
133
+ and the visitor has not switched it off. An id that nothing declares, in
134
+ `vendors`, on a script or iframe, or from the backend, reads as not allowed.
135
+ A typo such as `x-pixle` or a vendor you forgot to declare never looks like
136
+ consent. In development, c15t logs a console warning that names the id.
137
+
138
+ ## What c15t stores
139
+
140
+ c15t stores only the vendors a visitor switched off, and sends the full
141
+ vendor map to the backend with the consent record. Under an opt-out policy,
142
+ every vendor starts on. Turning a vendor off that was on reloads the page,
143
+ the same as withdrawing a category, so code the vendor already ran stops.
144
+
145
+ ## What switching a vendor off does not do
146
+
147
+ * It does not delete cookies the vendor already set.
148
+ [Clear on revocation](./clear-on-revocation.md) does not run, because the
149
+ category stays allowed. Delete the vendor's cookies from the script's
150
+ `onConsentChange` when `info.vendor.granted` is `false`.
151
+ * It does not expire. The switch stays off until the visitor changes it or
152
+ uses Accept All or Reject All, even across a policy change.
153
+ * Adding a vendor does not ask returning visitors again. A new vendor starts
154
+ on inside an allowed category. Change the policy's `copyRevision` if a new
155
+ vendor should prompt again.
156
+
157
+ ## Verify vendor consent
158
+
159
+ Test the production build in a private window with DevTools Network open and
160
+ filtered to `ads-twitter.com`:
161
+
162
+ 1. Declare `x-pixel` and register `xPixel()`. Open Privacy settings. The
163
+ marketing row lists X Pixel with a switch.
164
+ 2. Allow marketing and save. `uwt.js` loads.
165
+ 3. Open Privacy settings, switch X Pixel off and save. c15t reloads the
166
+ page. After the reload there is no `uwt.js` request, and other marketing
167
+ scripts still load.
168
+ 4. Reload again. The X Pixel switch is still off.
169
+ 5. Turn marketing off. The X Pixel switch is disabled.
170
+ 6. Open Privacy settings and click Accept All. `uwt.js` loads again.