@c15t/react 3.0.0-alpha.0 → 3.0.0-alpha.10

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 (405) hide show
  1. package/AGENTS.md +96 -69
  2. package/README.md +21 -17
  3. package/SKILL.md +34 -0
  4. package/dist/aggregate-components.js +1 -1
  5. package/dist/chunk-warming.js +1 -1
  6. package/dist/component-hooks/use-headless-consent-ui.js +1 -1
  7. package/dist/component-hooks/use-headless-iab-consent-ui.js +1 -1
  8. package/dist/component-hooks/use-panel-trigger.js +1 -1
  9. package/dist/components/consent-gate/atoms.js +1 -0
  10. package/dist/components/consent-gate/consent-gate.js +2 -0
  11. package/dist/components/consent-gate/frame.js +2 -0
  12. package/dist/components/consent-gate/index.js +2 -0
  13. package/dist/components/consent-preferences-link/consent-preferences-link.js +1 -1
  14. package/dist/components/iab-panel/atoms/card.js +1 -1
  15. package/dist/components/iab-panel/atoms/feature-item.js +2 -0
  16. package/dist/components/iab-panel/atoms/header.js +1 -1
  17. package/dist/components/iab-panel/atoms/purpose-item.js +1 -1
  18. package/dist/components/iab-panel/atoms/root.js +1 -1
  19. package/dist/components/iab-panel/atoms/stack-item.js +1 -1
  20. package/dist/components/iab-panel/atoms/vendor-list.js +1 -1
  21. package/dist/components/iab-panel/hooks/use-gvl-data.js +1 -1
  22. package/dist/components/iab-panel/iab-panel.js +1 -1
  23. package/dist/components/iab-panel/index.js +1 -1
  24. package/dist/components/iab-prompt/atoms/overlay.js +1 -1
  25. package/dist/components/iab-prompt/atoms/root.js +1 -1
  26. package/dist/components/panel/atoms/card.js +1 -1
  27. package/dist/components/panel/atoms/overlay.js +1 -1
  28. package/dist/components/panel/atoms/root.js +1 -1
  29. package/dist/components/panel/panel.js +1 -1
  30. package/dist/components/panel-trigger/atoms/button.js +1 -1
  31. package/dist/components/panel-trigger/atoms/icon.js +1 -1
  32. package/dist/components/panel-trigger/atoms/root.js +1 -1
  33. package/dist/components/panel-trigger/atoms/toolbar.js +1 -1
  34. package/dist/components/panel-trigger/atoms/trigger-icons.js +1 -0
  35. package/dist/components/panel-trigger/panel-trigger-toolbar.js +1 -1
  36. package/dist/components/panel-trigger/panel-trigger.js +1 -1
  37. package/dist/components/preferences/atoms/accordion.js +1 -1
  38. package/dist/components/preferences/atoms/root.js +1 -1
  39. package/dist/components/preferences/atoms/vendor-list.js +1 -0
  40. package/dist/components/preferences/index.js +1 -1
  41. package/dist/components/prompt/atoms/early-tap-script.js +2 -0
  42. package/dist/components/prompt/atoms/overlay.js +1 -1
  43. package/dist/components/prompt/atoms/root.js +1 -1
  44. package/dist/components/prompt/components.js +1 -1
  45. package/dist/components/prompt/policy-actions.js +1 -1
  46. package/dist/components/prompt/prompt.js +1 -1
  47. package/dist/components/shared/dialog-sheets.js +1 -0
  48. package/dist/components/shared/first-paint-sheets.js +1 -0
  49. package/dist/components/shared/iab-dialog-sheets.js +1 -0
  50. package/dist/components/shared/iab-first-paint-sheets.js +1 -0
  51. package/dist/components/shared/libs/compose-refs.js +1 -0
  52. package/dist/components/shared/libs/slot.js +1 -1
  53. package/dist/components/shared/primitives/button.js +1 -1
  54. package/dist/components/shared/primitives/legal-links/index.js +1 -1
  55. package/dist/components/shared/streamed-surface.js +2 -0
  56. package/dist/components/shared/surface-styles.js +2 -0
  57. package/dist/components/shared/ui/dialog/dialog.js +1 -1
  58. package/dist/components/shared/ui/icon.js +1 -1
  59. package/dist/components/shared/ui/logo.js +1 -1
  60. package/dist/components/shared/ui/preference-item/preference-item.js +1 -1
  61. package/dist/consent-gate.js +2 -0
  62. package/dist/consent-theme.js +1 -0
  63. package/dist/context/consent-tracking-context.js +1 -1
  64. package/dist/context/index.js +1 -1
  65. package/dist/context.js +1 -1
  66. package/dist/devtools-launcher.js +2 -0
  67. package/dist/devtools.js +1 -1
  68. package/dist/draft-context.js +2 -0
  69. package/dist/draft.js +1 -1
  70. package/dist/external-iab-context.js +1 -1
  71. package/dist/gpp.js +2 -0
  72. package/dist/headless.js +1 -1
  73. package/dist/hooks/use-color-scheme.js +1 -1
  74. package/dist/hooks/use-committed-ref.js +2 -0
  75. package/dist/hooks/use-focus-trap.js +1 -1
  76. package/dist/hooks/use-headless-iab-consent-ui.js +1 -1
  77. package/dist/hooks/use-iab-policy-guard.js +1 -0
  78. package/dist/hooks/use-late-entry.js +2 -0
  79. package/dist/hooks.js +1 -1
  80. package/dist/iab/styles.tw3.css +5 -151
  81. package/dist/iab-context.js +1 -1
  82. package/dist/index-parts/consent-banner.js +1 -0
  83. package/dist/index-parts/consent-dialog-link.js +1 -0
  84. package/dist/index-parts/consent-dialog-trigger.js +1 -0
  85. package/dist/index-parts/consent-dialog.js +1 -0
  86. package/dist/index-parts/consent-gate.js +1 -0
  87. package/dist/index-parts/core.js +1 -0
  88. package/dist/index-parts/draft.js +1 -0
  89. package/dist/index-parts/frame.js +1 -0
  90. package/dist/index-parts/hooks.js +1 -0
  91. package/dist/index-parts/hosted.js +1 -0
  92. package/dist/index-parts/module-hooks.js +1 -0
  93. package/dist/index-parts/offline.js +1 -0
  94. package/dist/index-parts/presets.js +1 -0
  95. package/dist/index-parts/provider.js +1 -0
  96. package/dist/index-parts/theme.js +1 -0
  97. package/dist/index-parts/translations.js +1 -0
  98. package/dist/index.js +1 -1
  99. package/dist/kernel-selector.js +1 -0
  100. package/dist/modes.js +1 -0
  101. package/dist/module-hooks/iframe-blocker.js +1 -1
  102. package/dist/module-hooks/network-blocker.js +1 -1
  103. package/dist/module-hooks/network-hold.js +2 -0
  104. package/dist/module-hooks/persistence.js +1 -1
  105. package/dist/panel-link.js +1 -1
  106. package/dist/panel-trigger.js +1 -1
  107. package/dist/panel.js +1 -1
  108. package/dist/postcss-tailwind3.js +1 -0
  109. package/dist/preferences.js +1 -2
  110. package/dist/provider.js +1 -1
  111. package/dist/server/fetch-ssr-data.js +1 -1
  112. package/dist/server/headers.js +1 -1
  113. package/dist/server/normalize-url.js +1 -1
  114. package/dist/styles.tw3.css +35 -159
  115. package/dist/transports/hosted.js +1 -0
  116. package/dist/transports/manifest.js +1 -0
  117. package/dist/transports/offline.js +1 -1
  118. package/dist/utils/deferred-module.js +2 -0
  119. package/dist/utils/merge-slot-props.js +1 -1
  120. package/dist/utils/theme-utils.js +1 -1
  121. package/dist/version.js +1 -1
  122. package/dist-types/aggregate-components.d.ts +2 -2
  123. package/dist-types/chunk-warming.d.ts +45 -8
  124. package/dist-types/component-hooks/use-headless-consent-ui.d.ts +5 -5
  125. package/dist-types/component-hooks/use-headless-iab-consent-ui.d.ts +2 -2
  126. package/dist-types/components/consent-gate/atoms.d.ts +11 -0
  127. package/dist-types/components/consent-gate/consent-gate.d.ts +2 -0
  128. package/dist-types/components/consent-gate/frame.d.ts +19 -0
  129. package/dist-types/components/consent-gate/index.d.ts +25 -0
  130. package/dist-types/components/{frame → consent-gate}/types.d.ts +7 -7
  131. package/dist-types/components/consent-preferences-link/consent-preferences-link.d.ts +1 -1
  132. package/dist-types/components/consent-preferences-link/index.d.ts +1 -1
  133. package/dist-types/components/iab-panel/atoms/feature-item.d.ts +28 -0
  134. package/dist-types/components/iab-panel/atoms/purpose-item.d.ts +5 -2
  135. package/dist-types/components/iab-panel/atoms/stack-item.d.ts +7 -2
  136. package/dist-types/components/iab-panel/atoms/vendor-list.d.ts +1 -1
  137. package/dist-types/components/iab-panel/iab-panel.d.ts +3 -3
  138. package/dist-types/components/iab-panel/index.d.ts +29 -26
  139. package/dist-types/components/iab-panel/use-iab-translations.d.ts +4 -0
  140. package/dist-types/components/iab-prompt/iab-prompt.d.ts +3 -3
  141. package/dist-types/components/iab-prompt/index.d.ts +19 -19
  142. package/dist-types/components/panel/atoms/card.d.ts +5 -4
  143. package/dist-types/components/panel/atoms/overlay.d.ts +1 -1
  144. package/dist-types/components/panel/index.d.ts +8 -8
  145. package/dist-types/components/panel/panel.d.ts +3 -3
  146. package/dist-types/components/panel-link/index.d.ts +1 -1
  147. package/dist-types/components/panel-trigger/atoms/button.d.ts +1 -1
  148. package/dist-types/components/panel-trigger/atoms/icon.d.ts +1 -1
  149. package/dist-types/components/panel-trigger/atoms/root.d.ts +2 -2
  150. package/dist-types/components/panel-trigger/atoms/toolbar.d.ts +2 -2
  151. package/dist-types/components/panel-trigger/atoms/trigger-icons.d.ts +25 -0
  152. package/dist-types/components/panel-trigger/index.d.ts +4 -4
  153. package/dist-types/components/panel-trigger/panel-trigger-toolbar.d.ts +1 -1
  154. package/dist-types/components/panel-trigger/panel-trigger.d.ts +9 -17
  155. package/dist-types/components/panel-trigger/types.d.ts +1 -1
  156. package/dist-types/components/preferences/atoms/accordion.d.ts +6 -4
  157. package/dist-types/components/preferences/atoms/button.d.ts +1 -1
  158. package/dist-types/components/preferences/atoms/footer.d.ts +1 -1
  159. package/dist-types/components/preferences/atoms/root.d.ts +1 -1
  160. package/dist-types/components/preferences/atoms/vendor-list.d.ts +23 -0
  161. package/dist-types/components/preferences/index.d.ts +14 -12
  162. package/dist-types/components/preferences/policy-actions.d.ts +2 -2
  163. package/dist-types/components/preferences/preferences.d.ts +1 -1
  164. package/dist-types/components/preferences/types.d.ts +1 -1
  165. package/dist-types/components/prompt/atoms/early-tap-script.d.ts +17 -0
  166. package/dist-types/components/prompt/atoms/root.d.ts +4 -0
  167. package/dist-types/components/prompt/components.d.ts +9 -3
  168. package/dist-types/components/prompt/index.d.ts +12 -12
  169. package/dist-types/components/prompt/policy-actions.d.ts +2 -2
  170. package/dist-types/components/prompt/prompt.d.ts +5 -1
  171. package/dist-types/components/shared/dialog-sheets.d.ts +9 -0
  172. package/dist-types/components/shared/first-paint-sheets.d.ts +8 -0
  173. package/dist-types/components/shared/iab-dialog-sheets.d.ts +3 -0
  174. package/dist-types/components/shared/iab-first-paint-sheets.d.ts +3 -0
  175. package/dist-types/components/shared/libs/compose-refs.d.ts +20 -0
  176. package/dist-types/components/shared/policy-actions.d.ts +2 -2
  177. package/dist-types/components/shared/primitives/box.d.ts +1 -1
  178. package/dist-types/components/shared/primitives/button.d.ts +2 -2
  179. package/dist-types/components/shared/primitives/button.types.d.ts +1 -1
  180. package/dist-types/components/shared/streamed-surface.d.ts +23 -0
  181. package/dist-types/components/shared/surface-styles.d.ts +36 -0
  182. package/dist-types/components/shared/ui/accordion/accordion.d.ts +2 -2
  183. package/dist-types/components/shared/ui/accordion/index.d.ts +2 -2
  184. package/dist-types/components/shared/ui/animated-collapse/index.d.ts +2 -2
  185. package/dist-types/components/shared/ui/branding.d.ts +1 -1
  186. package/dist-types/components/shared/ui/button/button.d.ts +1 -1
  187. package/dist-types/components/shared/ui/button/index.d.ts +2 -2
  188. package/dist-types/components/shared/ui/collapsible/index.d.ts +1 -1
  189. package/dist-types/components/shared/ui/dialog/index.d.ts +1 -1
  190. package/dist-types/components/shared/ui/icon.d.ts +2 -1
  191. package/dist-types/components/shared/ui/logo.d.ts +0 -10
  192. package/dist-types/components/shared/ui/preference-item/index.d.ts +1 -1
  193. package/dist-types/components/shared/ui/preference-item/preference-item.d.ts +13 -0
  194. package/dist-types/components/shared/ui/switch/index.d.ts +2 -2
  195. package/dist-types/components/shared/ui/switch/switch.d.ts +1 -1
  196. package/dist-types/components/shared/ui/tabs/index.d.ts +1 -1
  197. package/dist-types/consent-gate.d.ts +1 -0
  198. package/dist-types/consent-theme.d.ts +38 -0
  199. package/dist-types/context/consent-tracking-context.d.ts +10 -0
  200. package/dist-types/context/index.d.ts +3 -3
  201. package/dist-types/context/theme-context.d.ts +3 -3
  202. package/dist-types/context.d.ts +25 -1
  203. package/dist-types/devtools-launcher.d.ts +29 -0
  204. package/dist-types/devtools.d.ts +13 -2
  205. package/dist-types/draft-context.d.ts +31 -0
  206. package/dist-types/draft.d.ts +81 -9
  207. package/dist-types/gpp.d.ts +27 -0
  208. package/dist-types/headless.d.ts +6 -7
  209. package/dist-types/hooks/use-committed-ref.d.ts +24 -0
  210. package/dist-types/hooks/use-focus-trap.d.ts +3 -1
  211. package/dist-types/hooks/use-headless-iab-consent-ui.d.ts +1 -1
  212. package/dist-types/hooks/use-iab-manager.d.ts +5 -5
  213. package/dist-types/hooks/use-iab-policy-guard.d.ts +35 -0
  214. package/dist-types/hooks/use-late-entry.d.ts +14 -0
  215. package/dist-types/hooks/use-theme.d.ts +1 -1
  216. package/dist-types/hooks.d.ts +106 -11
  217. package/dist-types/iab.d.ts +7 -7
  218. package/dist-types/index-parts/consent-banner.d.ts +7 -0
  219. package/dist-types/index-parts/consent-dialog-link.d.ts +7 -0
  220. package/dist-types/index-parts/consent-dialog-trigger.d.ts +7 -0
  221. package/dist-types/index-parts/consent-dialog.d.ts +7 -0
  222. package/dist-types/index-parts/consent-gate.d.ts +7 -0
  223. package/dist-types/index-parts/core.d.ts +7 -0
  224. package/dist-types/index-parts/draft.d.ts +7 -0
  225. package/dist-types/index-parts/frame.d.ts +7 -0
  226. package/dist-types/index-parts/hooks.d.ts +7 -0
  227. package/dist-types/index-parts/hosted.d.ts +7 -0
  228. package/dist-types/index-parts/module-hooks.d.ts +7 -0
  229. package/dist-types/index-parts/offline.d.ts +7 -0
  230. package/dist-types/index-parts/presets.d.ts +7 -0
  231. package/dist-types/index-parts/provider.d.ts +7 -0
  232. package/dist-types/index-parts/theme.d.ts +8 -0
  233. package/dist-types/index-parts/translations.d.ts +7 -0
  234. package/dist-types/index.d.ts +32 -28
  235. package/dist-types/kernel-selector.d.ts +68 -0
  236. package/dist-types/modes.d.ts +26 -0
  237. package/dist-types/module-hooks/iframe-blocker.d.ts +33 -1
  238. package/dist-types/module-hooks/network-blocker.d.ts +23 -0
  239. package/dist-types/module-hooks/network-hold.d.ts +47 -0
  240. package/dist-types/module-hooks.d.ts +4 -4
  241. package/dist-types/panel-link.d.ts +1 -1
  242. package/dist-types/panel-trigger.d.ts +1 -1
  243. package/dist-types/panel.d.ts +13 -1
  244. package/dist-types/postcss-tailwind3.d.ts +21 -0
  245. package/dist-types/preferences.d.ts +12 -1
  246. package/dist-types/primitives/accordion.d.ts +1 -1
  247. package/dist-types/primitives/button.d.ts +1 -1
  248. package/dist-types/primitives/collapsible.d.ts +1 -1
  249. package/dist-types/primitives/dialog.d.ts +1 -1
  250. package/dist-types/primitives/preference-item.d.ts +1 -1
  251. package/dist-types/primitives/switch.d.ts +1 -1
  252. package/dist-types/primitives/tabs.d.ts +1 -1
  253. package/dist-types/primitives.d.ts +7 -7
  254. package/dist-types/prompt.d.ts +1 -1
  255. package/dist-types/provider.d.ts +174 -28
  256. package/dist-types/server/fetch-ssr-data.d.ts +1 -1
  257. package/dist-types/server/headers.d.ts +14 -4
  258. package/dist-types/server/index.d.ts +5 -5
  259. package/dist-types/server/normalize-url.d.ts +14 -7
  260. package/dist-types/server/types.d.ts +12 -1
  261. package/dist-types/theme-provider.d.ts +2 -2
  262. package/dist-types/transports/hosted.d.ts +36 -0
  263. package/dist-types/transports/manifest.d.ts +48 -0
  264. package/dist-types/transports/offline.d.ts +7 -16
  265. package/dist-types/types/index.d.ts +3 -3
  266. package/dist-types/types/manager.d.ts +1 -1
  267. package/dist-types/types/theme/index.d.ts +1 -1
  268. package/dist-types/ui-config-context.d.ts +14 -2
  269. package/dist-types/utils/deferred-module.d.ts +13 -0
  270. package/dist-types/utils/index.d.ts +4 -4
  271. package/dist-types/utils/merge-slot-props.d.ts +17 -3
  272. package/dist-types/utils/merge-styles.d.ts +1 -1
  273. package/dist-types/utils/theme-utils.d.ts +18 -3
  274. package/dist-types/version.d.ts +1 -1
  275. package/docs/README.md +96 -69
  276. package/docs/assets/v3/bottom-bar.png +0 -0
  277. package/docs/assets/v3/brand-card.png +0 -0
  278. package/docs/assets/v3/brand-preferences.png +0 -0
  279. package/docs/assets/v3/choice-wall.png +0 -0
  280. package/docs/assets/v3/headless-bar-html.png +0 -0
  281. package/docs/assets/v3/headless-bar-mobile.png +0 -0
  282. package/docs/assets/v3/headless-bar.png +0 -0
  283. package/docs/assets/v3/slim-bar.png +0 -0
  284. package/docs/concepts/choose-your-setup.md +138 -0
  285. package/docs/concepts/consent-categories.md +84 -0
  286. package/docs/concepts/consent-state.md +421 -0
  287. package/docs/concepts/data-fetching.md +270 -0
  288. package/docs/concepts/how-consent-works.md +123 -0
  289. package/docs/concepts/modes.md +278 -0
  290. package/docs/{frameworks/react/concepts/policy-presets.md → concepts/policies.md} +8 -60
  291. package/docs/customization/class-names.md +202 -0
  292. package/docs/customization/dark-mode.md +157 -0
  293. package/docs/customization/motion.md +128 -0
  294. package/docs/customization/overview.md +68 -33
  295. package/docs/customization/recipes.md +838 -47
  296. package/docs/customization/slots.md +217 -35
  297. package/docs/customization/stylesheets.md +248 -0
  298. package/docs/customization/tailwind.md +863 -0
  299. package/docs/customization/tokens.md +167 -36
  300. package/docs/customization/translations.md +87 -3
  301. package/docs/frameworks/react/callbacks.md +118 -0
  302. package/docs/frameworks/react/clear-on-revocation.md +106 -0
  303. package/docs/frameworks/react/components/consent-banner.md +48 -27
  304. package/docs/frameworks/react/components/consent-dialog-link.md +13 -11
  305. package/docs/frameworks/react/components/consent-dialog-trigger.md +86 -76
  306. package/docs/frameworks/react/components/consent-dialog.md +111 -58
  307. package/docs/frameworks/react/components/{frame.md → consent-gate.md} +70 -49
  308. package/docs/frameworks/react/components/consent-provider.md +140 -0
  309. package/docs/frameworks/react/components/consent-widget.md +69 -24
  310. package/docs/frameworks/react/components/dev-tools.md +76 -85
  311. package/docs/frameworks/react/components.md +113 -0
  312. package/docs/frameworks/react/compose.md +354 -0
  313. package/docs/frameworks/react/content-security-policy.md +143 -0
  314. package/docs/frameworks/react/customize.md +258 -0
  315. package/docs/frameworks/react/embeds.md +89 -0
  316. package/docs/frameworks/react/gpp.md +163 -0
  317. package/docs/frameworks/react/headless.md +4 -4
  318. package/docs/frameworks/react/hooks.md +70 -0
  319. package/docs/frameworks/react/iab.md +259 -0
  320. package/docs/frameworks/react/network-blocker.md +140 -0
  321. package/docs/frameworks/react/quickstart.md +220 -102
  322. package/docs/frameworks/react/rendering.md +263 -0
  323. package/docs/frameworks/react/scripts.md +114 -0
  324. package/docs/frameworks/react/translations.md +155 -0
  325. package/docs/frameworks/react/troubleshooting.md +188 -61
  326. package/docs/frameworks/react/upgrade-v3.md +651 -0
  327. package/docs/frameworks/react/vendor-consent.md +188 -0
  328. package/docs/guides/banner-experiments.md +672 -0
  329. package/docs/guides/troubleshooting.md +192 -54
  330. package/docs/guides/verify-consent.md +96 -49
  331. package/docs/integrations/adobe-analytics.md +177 -183
  332. package/docs/integrations/ahrefs-analytics.md +162 -178
  333. package/docs/integrations/amplitude.md +172 -180
  334. package/docs/integrations/building-integrations.md +137 -33
  335. package/docs/integrations/clearbit.md +164 -178
  336. package/docs/integrations/cloudflare-web-analytics.md +166 -180
  337. package/docs/integrations/cloudflare-zaraz.md +333 -0
  338. package/docs/integrations/crisp.md +174 -182
  339. package/docs/integrations/databuddy.md +167 -197
  340. package/docs/integrations/fathom-analytics.md +168 -180
  341. package/docs/integrations/front-chat.md +308 -0
  342. package/docs/integrations/google-maps.md +142 -108
  343. package/docs/integrations/google-tag-manager.md +258 -185
  344. package/docs/integrations/google-tag.md +226 -182
  345. package/docs/integrations/heap.md +173 -179
  346. package/docs/integrations/hightouch.md +171 -181
  347. package/docs/integrations/hotjar.md +169 -179
  348. package/docs/integrations/intercom.md +193 -177
  349. package/docs/integrations/klaviyo.md +473 -0
  350. package/docs/integrations/linkedin-insights.md +184 -174
  351. package/docs/integrations/logrocket.md +170 -180
  352. package/docs/integrations/matomo-analytics.md +198 -202
  353. package/docs/integrations/meta-pixel.md +198 -174
  354. package/docs/integrations/microsoft-clarity.md +173 -179
  355. package/docs/integrations/microsoft-uet.md +158 -178
  356. package/docs/integrations/mixpanel-analytics.md +165 -184
  357. package/docs/integrations/one-dollar-stats.md +292 -0
  358. package/docs/integrations/openai-pixel.md +214 -325
  359. package/docs/integrations/overview.md +152 -83
  360. package/docs/integrations/pinterest-tag.md +315 -0
  361. package/docs/integrations/pirsch.md +179 -183
  362. package/docs/integrations/plausible-analytics.md +182 -182
  363. package/docs/integrations/posthog.md +323 -264
  364. package/docs/integrations/promptwatch.md +164 -178
  365. package/docs/integrations/reddit-pixel.md +195 -181
  366. package/docs/integrations/rudderstack.md +211 -210
  367. package/docs/integrations/rybbit-analytics.md +181 -184
  368. package/docs/integrations/segment.md +192 -178
  369. package/docs/integrations/sentry.md +402 -0
  370. package/docs/integrations/snapchat-pixel.md +196 -180
  371. package/docs/integrations/tiktok-pixel.md +181 -174
  372. package/docs/integrations/umami-analytics.md +173 -182
  373. package/docs/integrations/vercel-analytics.md +176 -181
  374. package/docs/integrations/x-pixel.md +186 -174
  375. package/docs/integrations/youtube.md +150 -111
  376. package/package.json +48 -17
  377. package/dist/component-hooks/use-manager.js +0 -2
  378. package/dist/components/frame/atoms.js +0 -1
  379. package/dist/components/frame/frame.js +0 -2
  380. package/dist/components/frame/index.js +0 -2
  381. package/dist/frame.js +0 -2
  382. package/dist/ui-save.js +0 -1
  383. package/dist-types/component-hooks/use-manager.d.ts +0 -40
  384. package/dist-types/components/frame/atoms.d.ts +0 -11
  385. package/dist-types/components/frame/frame.d.ts +0 -2
  386. package/dist-types/components/frame/index.d.ts +0 -24
  387. package/dist-types/frame.d.ts +0 -1
  388. package/dist-types/ui-save.d.ts +0 -5
  389. package/docs/assets/v3/brand-bar.png +0 -0
  390. package/docs/assets/v3/mobile-card.png +0 -0
  391. package/docs/assets/v3/preferences.png +0 -0
  392. package/docs/frameworks/react/components/consent-manager-provider.md +0 -62
  393. package/docs/frameworks/react/concepts/consent-categories.md +0 -33
  394. package/docs/frameworks/react/hooks/use-consent-manager/overview.md +0 -58
  395. package/docs/frameworks/react/iab/overview.md +0 -53
  396. package/docs/frameworks/react/script-loader.md +0 -55
  397. package/docs/frameworks/react/styling/overview.md +0 -207
  398. package/docs/guides/consent-state.md +0 -60
  399. package/docs/guides/data-fetching.md +0 -163
  400. package/docs/guides/deployment-modes.md +0 -63
  401. package/docs/upgrade-v3.md +0 -334
  402. package/readme.json +0 -48
  403. package/src/iab/styles.css +0 -11
  404. package/src/styles.css +0 -10
  405. /package/dist/components/{frame → consent-gate}/types.js +0 -0
@@ -1,33 +1,22 @@
1
1
  ---
2
- title: Theme tokens and CSS
3
- description: Style c15t with semantic tokens and use the stylesheet that matches
4
- your CSS tooling.
2
+ title: Theme tokens
3
+ description: Change c15t's colors, type, radius, spacing, shadows and motion
4
+ with theme tokens, and see every --c15t-* variable with its default.
5
5
  group: customization
6
6
  ---
7
7
 
8
- ## Load the stock stylesheet once
8
+ ## Where tokens come from
9
9
 
10
- React, Next.js and Svelte provide `styles.css`. Import the adapter's stylesheet
11
- at the app's global entry point. Vue includes styles in its components; Astro
12
- adds styles through its integration.
13
-
14
- ```tsx
15
- import 'c15t/react/styles.css';
16
- ```
17
-
18
- The standard stylesheet places rules in `@layer components`. For Tailwind 3,
19
- use the `styles.tw3.css` entry instead of the standard stylesheet, between the
20
- components and utilities directives in your Tailwind entry:
21
-
22
- ```css
23
- @tailwind base;
24
- @tailwind components;
25
- @import 'c15t/react/styles.tw3.css';
26
- @tailwind utilities;
27
- ```
28
-
29
- Do not load both c15t stylesheet variants. For Tailwind 4 or unlayered CSS,
30
- inspect layer order before reaching for `!important`.
10
+ c15t's components take their colors from `--c15t-*` CSS variables, and most
11
+ of their fonts, radii, spacing, shadows and motion too. c15t's rules set the
12
+ defaults. You change them with a theme object or with CSS, and every part
13
+ that reads a token changes with it. Some values are still fixed for one
14
+ component, such as button padding, the radius of the "Secured by" tag and
15
+ several font sizes and weights, so a token change does not move them. Restyle
16
+ those parts through [slots](./slots.md) or your own CSS.
17
+ [Stylesheets and CSS layers](./stylesheets.md)
18
+ covers how c15t loads its rules, and [dark mode](./dark-mode.md)
19
+ covers the dark set of tokens.
31
20
 
32
21
  ## Set semantic values together
33
22
 
@@ -39,18 +28,117 @@ readable in each state.
39
28
  import { defineTheme } from '@c15t/ui/theme';
40
29
 
41
30
  export const theme = defineTheme({
42
- colors: { primary: '#2f6f4e' },
43
- radius: { lg: '4px' },
44
- consentActions: {
45
- primary: { variant: 'primary', mode: 'filled' },
46
- dismiss: { variant: 'neutral', mode: 'stroke' },
47
- },
31
+ colors: { primary: '#2f6f4e' },
32
+ radius: { lg: '4px' },
33
+ consentActions: {
34
+ primary: { variant: 'primary', mode: 'filled' },
35
+ dismiss: { variant: 'neutral', mode: 'stroke' },
36
+ },
48
37
  });
49
38
  ```
50
39
 
51
- Install `@c15t/ui` if importing its theme helper directly. Pass `theme` in your
52
- provider options. `consentActions` selects styling by action role. A per-action
53
- entry overrides `primary`, which overrides `default`.
40
+ Install `@c15t/ui` if importing its theme helper directly. Where the theme
41
+ goes depends on the framework:
42
+
43
+ | Framework | Tokens | `consentActions` |
44
+ | ------------------------------ | --------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------ |
45
+ | Next.js, TanStack Start, React | `<ConsentTheme theme={theme} />`, rendered on the server where the app has one | `theme` in the provider options |
46
+ | Nuxt, Vue | `theme` or `tokens` in the module or plugin options | Not available |
47
+ | Astro | `theme` in the integration options | The same `theme` |
48
+ | Svelte, SvelteKit | `generateThemeCSS(theme)` from `@c15t/ui/theme` on the server, in a `<style>` element, or `--c15t-*` variables in your stylesheet | `theme` on `ConsentProvider` (Svelte) or `ConsentRoot` (SvelteKit) |
49
+ | HTML, JavaScript | `ui.theme` | Not available |
50
+
51
+ React and Svelte providers do not turn tokens in their `theme` option into
52
+ CSS, and warn in development when a theme holds tokens but the page has no
53
+ `<style id="c15t-theme">`. Your framework's customize page shows the full
54
+ setup.
55
+
56
+ `consentActions` selects styling by action role. A per-action entry overrides
57
+ `primary`, which overrides `default`.
58
+
59
+ ## Combine a generated theme with your own CSS
60
+
61
+ `generateThemeCSS` writes its variables on `:root:root` and
62
+ `.c15t-theme-root.c15t-theme-root`, 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
+ `ConsentTheme` in React, Next.js and TanStack Start, Astro's `theme` option and
66
+ the script tag's `ui.theme`.
67
+
68
+ A `--c15t-*` variable you set on plain `:root` in your own CSS loses to a
69
+ generated theme that sets the same variable, even when your rule loads later.
70
+ Put the value in the theme, or raise your selector:
71
+
72
+ ```css
73
+ :root:root {
74
+ --c15t-primary: #2f6f4e;
75
+ }
76
+ ```
77
+
78
+ Scoped rules such as `[data-prompt] { --c15t-primary: ... }` set the variable
79
+ on the banner element itself, so they still apply inside it.
80
+
81
+ ## Every token
82
+
83
+ Every framework uses the same tokens. A theme object, such as `ConsentTheme`'s
84
+ `theme`, Astro's and Vue's `theme` or the script tag's `ui.theme`, takes the
85
+ theme key, such as `radius.lg`. Vue and Nuxt `tokens` take the CSS variable
86
+ name without the leading `--`, such as `c15t-radius-lg`. A stylesheet sets the
87
+ CSS variable itself. Your framework's customize page shows where each one goes.
88
+ [Motion and animation](./motion.md) explains the duration and
89
+ easing tokens.
90
+
91
+ | CSS variable | Theme key | Default |
92
+ | ----------------------------- | -------------------------------- | ----------------------------------------------- |
93
+ | `--c15t-primary` | `colors.primary` | `hsl(228, 100%, 60%)` |
94
+ | `--c15t-primary-hover` | `colors.primaryHover` | `hsl(228, 100%, 55%)` |
95
+ | `--c15t-surface` | `colors.surface` | `hsl(0, 0%, 100%)` |
96
+ | `--c15t-surface-hover` | `colors.surfaceHover` | `hsl(0, 0%, 98%)` |
97
+ | `--c15t-border` | `colors.border` | `hsl(0, 0%, 90%)` |
98
+ | `--c15t-border-hover` | `colors.borderHover` | `hsl(0, 0%, 85%)` |
99
+ | `--c15t-text` | `colors.text` | `hsl(0, 0%, 10%)` |
100
+ | `--c15t-text-muted` | `colors.textMuted` | `hsl(0, 0%, 40%)` |
101
+ | `--c15t-text-on-primary` | `colors.textOnPrimary` | auto-derived from `colors.primary` when omitted |
102
+ | `--c15t-overlay` | `colors.overlay` | `hsla(0, 0%, 0%, 0.5)` |
103
+ | `--c15t-switch-track` | `colors.switchTrack` | `hsl(0, 0%, 85%)` |
104
+ | `--c15t-switch-track-active` | `colors.switchTrackActive` | `colors.primary` |
105
+ | `--c15t-switch-thumb` | `colors.switchThumb` | `hsl(0, 0%, 100%)` |
106
+ | `--c15t-font-family` | `typography.fontFamily` | `system-ui, -apple-system, sans-serif` |
107
+ | `--c15t-font-size-sm` | `typography.fontSize.sm` | `0.875rem` |
108
+ | `--c15t-font-size-base` | `typography.fontSize.base` | `1rem` |
109
+ | `--c15t-font-size-lg` | `typography.fontSize.lg` | `1.125rem` |
110
+ | `--c15t-font-weight-normal` | `typography.fontWeight.normal` | `400` |
111
+ | `--c15t-font-weight-medium` | `typography.fontWeight.medium` | `500` |
112
+ | `--c15t-font-weight-semibold` | `typography.fontWeight.semibold` | `600` |
113
+ | `--c15t-line-height-tight` | `typography.lineHeight.tight` | `1.25` |
114
+ | `--c15t-line-height-normal` | `typography.lineHeight.normal` | `1.5` |
115
+ | `--c15t-line-height-relaxed` | `typography.lineHeight.relaxed` | `1.75` |
116
+ | `--c15t-space-xs` | `spacing.xs` | `0.25rem` |
117
+ | `--c15t-space-sm` | `spacing.sm` | `0.5rem` |
118
+ | `--c15t-space-md` | `spacing.md` | `1rem` |
119
+ | `--c15t-space-lg` | `spacing.lg` | `1.5rem` |
120
+ | `--c15t-space-xl` | `spacing.xl` | `2rem` |
121
+ | `--c15t-radius-sm` | `radius.sm` | `0.25rem` |
122
+ | `--c15t-radius-md` | `radius.md` | `0.5rem` |
123
+ | `--c15t-radius-lg` | `radius.lg` | `0.75rem` |
124
+ | `--c15t-radius-full` | `radius.full` | `9999px` |
125
+ | `--c15t-shadow-sm` | `shadows.sm` | `0 1px 2px hsla(0, 0%, 0%, 0.05)` |
126
+ | `--c15t-shadow-md` | `shadows.md` | `0 4px 12px hsla(0, 0%, 0%, 0.08)` |
127
+ | `--c15t-shadow-lg` | `shadows.lg` | `0 8px 24px hsla(0, 0%, 0%, 0.12)` |
128
+ | `--c15t-duration-fast` | `motion.duration.fast` | `80ms` |
129
+ | `--c15t-duration-normal` | `motion.duration.normal` | `150ms` |
130
+ | `--c15t-duration-slow` | `motion.duration.slow` | `200ms` |
131
+ | `--c15t-easing` | `motion.easing` | `cubic-bezier(0.4, 0, 0.2, 1)` |
132
+ | `--c15t-easing-out` | `motion.easingOut` | `cubic-bezier(0.215, 0.61, 0.355, 1)` |
133
+ | `--c15t-easing-in` | `motion.easingIn` | `cubic-bezier(0.55, 0.055, 0.675, 0.19)` |
134
+ | `--c15t-easing-in-out` | `motion.easingInOut` | `cubic-bezier(0.645, 0.045, 0.355, 1)` |
135
+ | `--c15t-easing-spring` | `motion.easingSpring` | `cubic-bezier(0.34, 1.56, 0.64, 1)` |
136
+
137
+ The radius tokens round different parts. `radius.lg` rounds the banner card,
138
+ the preference dialog, `ConsentGate` placeholders and the floating trigger.
139
+ `radius.md` rounds buttons, accordions, tabs and the vendor list.
140
+ `radius.sm` rounds small parts inside the banner and dialog. To give the banner
141
+ and its buttons the same 4px corners, set both `lg` and `md`.
54
142
 
55
143
  ## Target a prompt with CSS
56
144
 
@@ -63,8 +151,9 @@ entry overrides `primary`, which overrides `default`.
63
151
  ```
64
152
 
65
153
  Use attributes exposed by the rendered component, not guessed class names.
66
- Test the prompt and the preferences dialog separately because tokens scoped to
67
- one prompt do not automatically reach a portaled dialog.
154
+ The script tag's banner has no `data-prompt` or `data-model`. Test the prompt
155
+ and the preferences dialog separately because tokens scoped to one prompt do
156
+ not automatically reach a portaled dialog.
68
157
 
69
158
  | Size variable | Default | Target |
70
159
  | ----------------------------------- | ------- | ------------- |
@@ -72,5 +161,47 @@ one prompt do not automatically reach a portaled dialog.
72
161
  | `--consent-banner-widget-max-width` | `20rem` | Widget |
73
162
  | `--consent-banner-wall-max-width` | `30rem` | Choice wall |
74
163
 
164
+ The banner footer lays out its actions by the card's width, not the
165
+ viewport's. With the default `compact` profile, a card narrower than 22rem
166
+ puts Reject and Accept on one row and Customize on a full-width row below
167
+ them, on any screen size.
168
+
75
169
  Test long translations and small screens after changing width or typography.
76
170
  A compact banner must still fit the required actions.
171
+
172
+ ## Restyle the "Secured by" tag
173
+
174
+ The tag sits on the edge of the banner and dialog cards and uses the primary
175
+ color by default. Set these variables on `:root`, or on an element that
176
+ contains the tag. The dialog renders in a portal, so a variable set on the
177
+ banner does not reach the dialog's tag.
178
+
179
+ | Variable | Default | Target |
180
+ | -------------------------------------------- | --------------------------------------- | -------------------------------------- |
181
+ | `--consent-branding-tag-background-color` | `var(--c15t-primary)` | Tag background |
182
+ | `--consent-branding-tag-border-color` | `--c15t-primary` mixed 14% toward black | Tag border |
183
+ | `--consent-branding-tag-text-color` | `var(--c15t-text-on-primary, #fff)` | "Secured by" and the wordmark |
184
+ | `--consent-branding-tag-mark-color` | The text color | c15t mark or inth logo |
185
+ | `--consent-branding-tag-shadow` | Inset highlight and a 1px drop shadow | Tag shadow |
186
+ | `--consent-branding-tag-attached-edge-width` | `0px` | Border on the edge that meets the card |
187
+
188
+ The stylesheet does not declare these variables. Each default resolves on the
189
+ tag, so a `--c15t-primary` you scope to a banner still colors the tag.
190
+
191
+ This makes the tag look like a tab of the card:
192
+
193
+ ```css
194
+ :root {
195
+ --consent-branding-tag-background-color: var(--c15t-surface);
196
+ --consent-branding-tag-border-color: var(--c15t-border);
197
+ --consent-branding-tag-text-color: var(--c15t-text-muted);
198
+ --consent-branding-tag-mark-color: var(--c15t-primary);
199
+ --consent-branding-tag-shadow: none;
200
+ }
201
+ ```
202
+
203
+ The edge that meets the card has no border by default. Above the banner the
204
+ tag overlaps the card's top border by 1px and covers it. Below the dialog the
205
+ tag starts under the card's bottom border. Set
206
+ `--consent-branding-tag-attached-edge-width: 1px` to draw that edge. It is
207
+ drawn over the card's border, so the two borders do not stack.
@@ -27,9 +27,52 @@ i18n: {
27
27
 
28
28
  Supply the same message keys in each supported locale. A one-off component prop
29
29
  such as `dismissButtonText` is useful for one banner; use translations for a
30
- site-wide change. Astro's serializable integration options and Vue's module
31
- configuration have their own types, so verify those shapes before copying a
32
- React object.
30
+ site-wide change. Astro's serializable integration options have their own
31
+ types, so verify that shape before copying a React object. The Vue plugin and
32
+ Nuxt module have no `i18n` option; their copy comes from the backend.
33
+
34
+ ## Load only the languages you use
35
+
36
+ c15t bundles English. Every other language it ships is a separate entry point
37
+ in `@c15t/translations`, named by its language code. Importing one registers
38
+ c15t's built-in copy for that language. Offline mode can then switch to it,
39
+ and your messages that repeat its wording don't hide backend edits. Import
40
+ them in the file that configures the provider, so they load in the browser:
41
+
42
+ ```ts
43
+ import '@c15t/translations/de';
44
+ import '@c15t/translations/fr';
45
+ ```
46
+
47
+ Each entry also exports that copy as `translations`, if you want to read a
48
+ string or build your messages from it:
49
+
50
+ ```ts
51
+ import { translations as de } from '@c15t/translations/de';
52
+ ```
53
+
54
+ The entries are `bg`, `cs`, `cy`, `da`, `de`, `el`, `es`, `et`, `fi`, `fr`,
55
+ `ga`, `gu`, `he`, `hi`, `hr`, `hu`, `id`, `is`, `it`, `lb`, `lt`, `lv`, `mt`,
56
+ `nb`, `nl`, `nn`, `pl`, `pt`, `rm`, `ro`, `sk`, `sl`, `sv` and `zh`.
57
+ `@c15t/translations/all` still loads every language at once. The `c15t`
58
+ package doesn't re-export these entries, so add `@c15t/translations` to your
59
+ dependencies.
60
+
61
+ ## Combine messages with backend copy
62
+
63
+ With a backend or a manifest, the copy it sends for the visitor's language is
64
+ the base. Your `i18n.messages` for that language replace it key by key, and
65
+ keys you leave out keep the backend's wording, including copy edited in your
66
+ Inth project. c15t looks up messages for the exact language first, then for
67
+ its primary language, so `de-AT` uses your `de` messages when there is no
68
+ `de-AT` entry. Messages for other languages are not applied.
69
+
70
+ A key only overrides the backend when your text differs from c15t's built-in
71
+ wording for that language. So passing a stock bundle to enable a language
72
+ keeps backend edits visible, while a key you actually reworded stays pinned
73
+ in code. c15t only recognizes the built-in wording of English and of the
74
+ languages you [import](#load-only-the-languages-you-use), such as
75
+ `@c15t/translations/de` or `@c15t/translations/all`.
33
76
 
34
77
  ## Write labels that describe the action
35
78
 
@@ -41,6 +84,47 @@ The notice acknowledgement uses `common.acknowledge`, with `common.dismiss` as
41
84
  a fallback for older translation bundles. Keep the displayed label and the
42
85
  command's effect aligned.
43
86
 
87
+ ## Translate the ConsentGate placeholder
88
+
89
+ The `ConsentGate` placeholder reads the `consentGate` section:
90
+
91
+ | Key | Where it shows |
92
+ | --------------------------- | ------------------------------------------------------------------------------------------------------------------- |
93
+ | `consentGate.title` | The placeholder text. `{category}` is replaced by the category's translated title. |
94
+ | `consentGate.actionButton` | The button that opens preferences, with the same `{category}` replacement. |
95
+ | `consentGate.policyBlocked` | React and Vue show it in place of the title, with no button, when a strict policy leaves the category out of scope. |
96
+
97
+ Earlier versions called this section `frame`. Copy under `frame` in
98
+ `i18n.messages` or custom translations still applies. c15t reads it as `consentGate`, a key set under `consentGate`
99
+ wins over the same key under `frame`, and c15t logs a warning once outside
100
+ production. Rename the section to `consentGate` to remove the warning.
101
+
102
+ ## Show right-to-left languages
103
+
104
+ c15t sets `dir="rtl"` on its surfaces when the resolved language is Arabic,
105
+ Hebrew, Persian, Urdu, Pashto, Sindhi, Kurdish or Dhivehi. It matches the
106
+ primary language, so `ar-EG` counts as Arabic. Hebrew is the only
107
+ right-to-left translation c15t ships. For the others, supply your own
108
+ messages or the copy from your Inth project.
109
+
110
+ What follows the direction:
111
+
112
+ * Text, headings and the button row in the banner, the preference dialog and
113
+ the preference widget flow right to left.
114
+ * A floating or widget banner in its default corner moves to the mirrored
115
+ corner, so `bottom-left` becomes `bottom-right`. A `position` you set
116
+ yourself stays where you put it.
117
+ * The HTML script tag, `@c15t/browser` and Astro's banner also set `lang` on
118
+ their surfaces. React, Vue and Svelte set `dir` only, so set `lang` on
119
+ `<html>` yourself.
120
+
121
+ What does not follow yet:
122
+
123
+ * The IAB TCF dialog aligns several labels, indents and borders to the left.
124
+ * The floating `ConsentDialogTrigger` keeps the corner you give it.
125
+
126
+ Test right-to-left pages with a real translation before you ship them.
127
+
44
128
  ## Test more than English
45
129
 
46
130
  Try the longest labels you support at a narrow width, with browser zoom and
@@ -0,0 +1,118 @@
1
+ ---
2
+ title: Callbacks
3
+ description: Run code in a React app when a visitor records a consent choice,
4
+ when permissions change, when a request fails and before the revocation
5
+ reload, with the ConsentProvider callbacks option.
6
+ group: frameworks
7
+ ---
8
+
9
+ ## Pass callbacks to the provider
10
+
11
+ Keep the callbacks in their own module:
12
+
13
+ ```ts title="src/consent-callbacks.ts"
14
+ import type { ConsentProviderCallbacks } from 'c15t/react';
15
+
16
+ export const callbacks = {
17
+ // Right before the page reloads because a save withdrew a permission.
18
+ onBeforeConsentRevocationReload: ({ preferences }) => {
19
+ console.info('Reloading with', preferences);
20
+ },
21
+ // The visitor clicked Accept all, Reject all or Save.
22
+ onChoiceRecorded: ({ confirmed, snapshot }) => {
23
+ console.info('Choice recorded', confirmed, snapshot.explicitChoice);
24
+ },
25
+ // A consent command failed, such as a save the backend rejected.
26
+ onError: ({ error }) => {
27
+ console.error('c15t', error);
28
+ },
29
+ // Permissions changed for any reason: a choice, an expired choice, a new
30
+ // policy or a privacy signal.
31
+ onPermissionsChanged: ({ previous, snapshot }) => {
32
+ console.info('Permissions', previous, snapshot.effectivePermissions);
33
+ },
34
+ } satisfies ConsentProviderCallbacks;
35
+ ```
36
+
37
+ Pass them in the `ConsentProvider` options, in the file that renders the
38
+ provider, such as `src/consent.tsx` from the
39
+ [quickstart](./quickstart.md):
40
+
41
+ ```tsx title="src/consent.tsx"
42
+ <ConsentProvider options={{ mode, scripts, callbacks }}>
43
+ ```
44
+
45
+ The provider subscribes the callbacks after it mounts, so they run in the
46
+ browser. If you render on the server, server rendering never calls them. The
47
+ provider calls the latest `callbacks` object it received, so a new object on
48
+ a later render takes effect without a remount. Defining the object at module
49
+ level, as above, keeps it the same across renders.
50
+
51
+ When you pass your own `runtime` to `ConsentProvider`, the provider does not
52
+ wire `callbacks` or `reloadOnConsentRevoked` from its options. Pass them to
53
+ `createConsentRuntime` instead.
54
+
55
+ ## Available callbacks
56
+
57
+ | Callback | Runs when | Receives |
58
+ | --------------------------------- | ----------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
59
+ | `onChoiceRecorded` | The visitor clicks Accept All, Reject All or Save Settings, in the stock UI or through `useSaveConsents()`. | `snapshot`, the state after the choice; `confirmed`, the categories this action recorded; `actionAt`, the time as a millisecond timestamp |
60
+ | `onPermissionsChanged` | Any effective permission changes value. | `snapshot`, the state after the change; `previous`, the permissions before it |
61
+ | `onError` | A consent command fails, such as a failed `/init` request or a save the backend rejects. | `error`, a message string |
62
+ | `onBeforeConsentRevocationReload` | A save withdrew a granted category or vendor, right before c15t reloads the page. | `preferences`, the permissions after the save |
63
+
64
+ The type for the whole object is `ConsentProviderCallbacks` from `c15t/react`.
65
+ Callbacks run after c15t updates its state, so `snapshot` and hooks such as
66
+ `useConsent()` already hold the new values.
67
+
68
+ ## onChoiceRecorded or onPermissionsChanged
69
+
70
+ `onChoiceRecorded` runs only for a visitor's action. `onPermissionsChanged`
71
+ runs whenever effective permissions change, whatever the cause:
72
+
73
+ | Event | `onChoiceRecorded` | `onPermissionsChanged` |
74
+ | --------------------------------------------------------- | ------------------ | ---------------------------- |
75
+ | Visitor accepts, rejects or saves | Yes | Yes, if a permission changed |
76
+ | A policy resolves and allows categories before any choice | No | Yes |
77
+ | Global Privacy Control turns off a grant | No | Yes |
78
+ | A recorded choice expires | No | Yes |
79
+ | Visitor dismisses a notice | No | No |
80
+
81
+ Use `onChoiceRecorded` for consent analytics and audit trails, because it
82
+ fires only when the visitor chose. Use `onPermissionsChanged` to start or stop
83
+ your own code. Never treat it as proof that the visitor agreed to anything,
84
+ and never save consent from it.
85
+ [How consent works](../../concepts/how-consent-works.md#a-permission-is-not-a-recorded-choice)
86
+ explains the difference.
87
+
88
+ Dismissing a notice records no choice and changes no permission. Read it with
89
+ `useNoticeDismissal()` instead. For consent inside one component, use the
90
+ [hooks](./hooks.md), which re-render the component when
91
+ consent changes.
92
+
93
+ ## Before a revocation reload
94
+
95
+ Removing a script tag cannot stop code that already ran, so c15t reloads the
96
+ page after an accept, reject or save turns off a category or vendor that was
97
+ granted. The reload waits for the save request to finish. Right before it,
98
+ `onBeforeConsentRevocationReload` runs synchronously. Use it to call a
99
+ vendor's shutdown API or flush queued events. Keep it short, because the
100
+ reload does not wait for promises.
101
+
102
+ A choice that expires, a new policy and a privacy signal change permissions
103
+ without a visitor action, so they do not reload the page.
104
+
105
+ To handle revocation yourself, set `reloadOnConsentRevoked: false` in the
106
+ provider options. Code the visitor turned off then keeps running until the
107
+ next full page load, and `onBeforeConsentRevocationReload` never runs.
108
+
109
+ ## Verify
110
+
111
+ 1. Add the callbacks from this page, open the console and DevTools Network,
112
+ clear site data and reload under a policy that shows a banner.
113
+ 2. Click **Accept All**. `onChoiceRecorded` and `onPermissionsChanged` both log,
114
+ and the save request to your backend appears in Network.
115
+ 3. Reload. `onChoiceRecorded` does not log again.
116
+ 4. Open Privacy settings, turn off a category you allowed and save.
117
+ `onBeforeConsentRevocationReload` logs, then the page reloads. After the
118
+ reload, that category's vendor requests are absent from Network.
@@ -0,0 +1,106 @@
1
+ ---
2
+ title: Clear on revocation
3
+ description: Delete the first-party cookies and Web Storage keys a consent
4
+ category owns in a React app when that category is denied, with the
5
+ clearOnRevocation option on ConsentProvider.
6
+ group: frameworks
7
+ ---
8
+
9
+ ## Configure cleanup
10
+
11
+ Gating stops new requests, but it leaves the cookies and storage keys a vendor
12
+ already wrote. List the ones each optional category owns and add them to the
13
+ provider options in `src/consent.tsx`. When that category is denied, c15t
14
+ deletes them:
15
+
16
+ ```tsx title="src/consent.tsx (partial)"
17
+ import type { ClearOnRevocationConfig } from 'c15t';
18
+
19
+ const clearOnRevocation: ClearOnRevocationConfig = {
20
+ measurement: { cookies: ['_ga', '_ga_*'], localStorage: ['analytics:*'] },
21
+ marketing: { cookies: ['_fbp'], sessionStorage: ['campaign-id'] },
22
+ };
23
+
24
+ <ConsentProvider options={{ mode, scripts, clearOnRevocation }}>
25
+ ```
26
+
27
+ The provider reads `clearOnRevocation` once, when it mounts; remount it to
28
+ change the list. A provider that borrows a `runtime` does not read it.
29
+ Configure cleanup on the runtime owner instead, with
30
+ `createConsentRuntime({ clearOnRevocation })`.
31
+
32
+ ## Name cookies and keys
33
+
34
+ Each optional category takes three lists. Without `clearOnRevocation`, c15t
35
+ deletes nothing, and `necessary` cannot be configured.
36
+
37
+ | Field | Takes |
38
+ | ---------------- | ------------------------------------------------------------------------------------------------------------ |
39
+ | `cookies` | Cookie names, prefixes ending in `*` such as `'_ga_*'`, or `{ name, domain?, path?, partitioned? }` objects. |
40
+ | `localStorage` | Keys, or prefixes ending in `*`. |
41
+ | `sessionStorage` | Keys, or prefixes ending in `*`. |
42
+
43
+ Use an exact name, or a prefix followed by `*`. `_ga_*` matches `_ga_ABC123`
44
+ but not `_ga`, so list both. Regular expressions, a `*` anywhere but the end,
45
+ and a bare `*` are not supported. Cookie names are matched as written,
46
+ without URL decoding.
47
+
48
+ By default c15t tries the current host and its parent domains, and the
49
+ current path and its parents. For a cookie set on a specific scope, use an
50
+ object:
51
+
52
+ ```ts
53
+ const cookies = [
54
+ { name: 'analytics-id', domain: 'example.com', path: '/' },
55
+ { name: 'checkout-metrics', domain: '', path: '/checkout' },
56
+ { name: 'partitioned-metrics', partitioned: true },
57
+ ];
58
+ ```
59
+
60
+ An empty `domain` means a host-only cookie. A `domain` or `path` you set
61
+ replaces the automatic attempts for that field. Partitioned cookies need
62
+ `partitioned: true`. A prefix only finds cookies the current page can read,
63
+ so use an exact name for a cookie on another path.
64
+
65
+ c15t never deletes its own consent records, even when a pattern matches
66
+ them.
67
+
68
+ ## When cleanup runs
69
+
70
+ * On the first page load after the policy resolves, for every denied
71
+ category. This covers a new opt-in visitor and a returning visitor whose
72
+ choice expired.
73
+ * Whenever a category changes from allowed to denied, after a saved refusal,
74
+ expiry, a policy change or a Global Privacy Control signal.
75
+
76
+ Cleanup does not run during server rendering, while the policy is still
77
+ loading, on draft switch changes in the dialog, or when the provider
78
+ unmounts. It runs once per change and does not poll. Under an opt-out
79
+ policy, an expired choice that stays allowed deletes nothing. If a resolved
80
+ policy later falls back to denial after a failed request, cleanup deletes
81
+ the data, and a successful retry cannot restore it.
82
+
83
+ Switching off one vendor inside an allowed category deletes nothing, because
84
+ the category stays allowed.
85
+
86
+ ## Browser limits
87
+
88
+ c15t can delete first-party cookies that JavaScript can read, and keys in
89
+ the current origin's `localStorage` and `sessionStorage`. It cannot delete
90
+ `HttpOnly` cookies or another origin's data; expire those from your server.
91
+ A vendor SDK still running on the page can write its cookies again, which is
92
+ one reason c15t reloads the page after a revocation. Deletion failures do
93
+ not block the consent update. See
94
+ [MDN on cookies](https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/Cookies).
95
+
96
+ ## Verify cleanup
97
+
98
+ 1. Allow measurement and let Google Analytics run. In DevTools Application,
99
+ Cookies shows `_ga` and a `_ga_<id>` cookie.
100
+ 2. Open Privacy settings, turn measurement off and save. c15t reloads the
101
+ page. Both cookies are gone, and `analytics:*` keys are gone from Local
102
+ storage.
103
+ 3. Reload. The cookies do not come back, because Google Analytics no longer
104
+ loads.
105
+ 4. If you listed a cookie with an explicit `domain` or `path`, repeat step 2
106
+ and check that the cookie is gone from that scope too.